<!DOCTYPE html>
<html lang="">
    <head>
        <meta charset="utf-8">
        <meta name="viewport" content="width=device-width, initial-scale=1">
        <meta name="robots" content="noodp" />
        <meta http-equiv="X-UA-Compatible" content="IE=edge, chrome=1">
        <title>JAVA NIO - 暗夜零星</title><meta name="Description" content="暗夜零星，java程序员。这里是我的个人博客，交流技术，分享经验。"><meta property="og:title" content="JAVA NIO" />
<meta property="og:description" content="Java NIO 基础知识" />
<meta property="og:type" content="article" />
<meta property="og:url" content="https://wlizhi.cc/posts/network/netty01-nio/" /><meta property="og:image" content="https://wlizhi.cc/logo.png"/><meta property="article:section" content="posts" />
<meta property="article:published_time" content="2024-05-26T06:25:30+08:00" />
<meta property="article:modified_time" content="2024-05-26T06:25:30+08:00" />

<meta name="twitter:card" content="summary_large_image"/>
<meta name="twitter:image" content="https://wlizhi.cc/logo.png"/>

<meta name="twitter:title" content="JAVA NIO"/>
<meta name="twitter:description" content="Java NIO 基础知识"/>
<meta name="application-name" content="暗夜零星">
<meta name="apple-mobile-web-app-title" content="暗夜零星"><link rel="shortcut icon" type="image/x-icon" href="/favicon.ico" />
        <link rel="icon" type="image/png" sizes="32x32" href="/favicon-32x32.png">
        <link rel="icon" type="image/png" sizes="16x16" href="/favicon-16x16.png"><link rel="apple-touch-icon" sizes="180x180" href="/apple-touch-icon.png"><link rel="manifest" href="/site.webmanifest"><link rel="canonical" href="https://wlizhi.cc/posts/network/netty01-nio/" /><link rel="prev" href="https://wlizhi.cc/posts/network/http-connect/" /><link rel="next" href="https://wlizhi.cc/posts/network/netty02-%E5%85%A5%E9%97%A8/" /><link rel="stylesheet" href="/lib/normalize/normalize.min.css"><link rel="stylesheet" href="/css/style.min.css"><link rel="stylesheet" href="/lib/fontawesome-free/all.min.css"><link rel="stylesheet" href="/lib/animate/animate.min.css"><script type="application/ld+json">
    {
        "@context": "http://schema.org",
        "@type": "BlogPosting",
        "headline": "JAVA NIO",
        "inLanguage": "",
        "mainEntityOfPage": {
            "@type": "WebPage",
            "@id": "https:\/\/wlizhi.cc\/posts\/network\/netty01-nio\/"
        },"genre": "posts","keywords": "网络, Netty","wordcount":  11950 ,
        "url": "https:\/\/wlizhi.cc\/posts\/network\/netty01-nio\/","datePublished": "2024-05-26T06:25:30+08:00","dateModified": "2024-05-26T06:25:30+08:00","publisher": {
            "@type": "Organization",
            "name": "Wlizhi"},"author": {
                "@type": "Person",
                "name": "Wlizhi"
            },"description": ""
    }
    </script></head>
    <body header-desktop="fixed" header-mobile="auto"><script type="text/javascript">(window.localStorage && localStorage.getItem('theme') ? localStorage.getItem('theme') === 'dark' : ('auto' === 'auto' ? window.matchMedia('(prefers-color-scheme: dark)').matches : 'auto' === 'dark')) && document.body.setAttribute('theme', 'dark');</script>

        <div id="mask"></div><div class="wrapper"><header class="desktop" id="header-desktop">
    <div class="header-wrapper">
        <div class="header-title">
            <a href="/" title="暗夜零星"><span class="header-title-pre"><i class='far fa-moon fa-fw'></i></span>暗夜零星</a>
        </div>
        <div class="menu">
            <div class="menu-inner"><a class="menu-item" href="/posts/"> 所有文章 </a><a class="menu-item" href="/tags/"> 标签 </a><a class="menu-item" href="/categories/"> 分类 </a><a class="menu-item" href="/message-board/"> 留言 </a><a class="menu-item" href="/about/"> 关于 </a><a class="menu-item" href="https://github.com/wlizhi" title="GitHub" rel="noopener noreffer" target="_blank"><i class='fab fa-github fa-fw'></i>  </a><a class="menu-item" href="https://gitee.com/wlizhi" title="Gitee" rel="noopener noreffer" target="_blank"><i class='fab fa-git fa-fw'></i>  </a><span class="menu-item delimiter"></span><span class="menu-item search" id="search-desktop">
                        <input type="text" placeholder="点击搜索..." id="search-input-desktop">
                        <a href="javascript:void(0);" class="search-button search-toggle" id="search-toggle-desktop" title="搜索">
                            <i class="fas fa-search fa-fw"></i>
                        </a>
                        <a href="javascript:void(0);" class="search-button search-clear" id="search-clear-desktop" title="清空">
                            <i class="fas fa-times-circle fa-fw"></i>
                        </a>
                        <span class="search-button search-loading" id="search-loading-desktop">
                            <i class="fas fa-spinner fa-fw fa-spin"></i>
                        </span>
                    </span><a href="javascript:void(0);" class="menu-item theme-switch" title="切换主题">
                    <i class="fas fa-adjust fa-fw"></i>
                </a>
            </div>
        </div>
    </div>
</header><header class="mobile" id="header-mobile">
    <div class="header-container">
        <div class="header-wrapper">
            <div class="header-title">
                <a href="/" title="暗夜零星"><span class="header-title-pre"><i class='far fa-moon fa-fw'></i></span>暗夜零星</a>
            </div>
            <div class="menu-toggle" id="menu-toggle-mobile">
                <span></span><span></span><span></span>
            </div>
        </div>
        <div class="menu" id="menu-mobile"><div class="search-wrapper">
                    <div class="search mobile" id="search-mobile">
                        <input type="text" placeholder="点击搜索..." id="search-input-mobile">
                        <a href="javascript:void(0);" class="search-button search-toggle" id="search-toggle-mobile" title="搜索">
                            <i class="fas fa-search fa-fw"></i>
                        </a>
                        <a href="javascript:void(0);" class="search-button search-clear" id="search-clear-mobile" title="清空">
                            <i class="fas fa-times-circle fa-fw"></i>
                        </a>
                        <span class="search-button search-loading" id="search-loading-mobile">
                            <i class="fas fa-spinner fa-fw fa-spin"></i>
                        </span>
                    </div>
                    <a href="javascript:void(0);" class="search-cancel" id="search-cancel-mobile">
                        取消
                    </a>
                </div><a class="menu-item" href="/posts/" title="">所有文章</a><a class="menu-item" href="/tags/" title="">标签</a><a class="menu-item" href="/categories/" title="">分类</a><a class="menu-item" href="/message-board/" title="">留言</a><a class="menu-item" href="/about/" title="">关于</a><a class="menu-item" href="https://github.com/wlizhi" title="GitHub" rel="noopener noreffer" target="_blank"><i class='fab fa-github fa-fw'></i></a><a class="menu-item" href="https://gitee.com/wlizhi" title="Gitee" rel="noopener noreffer" target="_blank"><i class='fab fa-git fa-fw'></i></a><a href="javascript:void(0);" class="menu-item theme-switch" title="切换主题">
                <i class="fas fa-adjust fa-fw"></i>
            </a></div>
    </div>
</header>
<div class="search-dropdown desktop">
    <div id="search-dropdown-desktop"></div>
</div>
<div class="search-dropdown mobile">
    <div id="search-dropdown-mobile"></div>
</div>
<main class="main">
                <div class="container"><div class="toc" id="toc-auto">
            <h2 class="toc-title">目录</h2>
            <div class="toc-content" id="toc-content-auto"></div>
        </div><article class="page single"><h1 class="single-title animated flipInX">JAVA NIO</h1><div class="post-meta">
            <div class="post-meta-line"><span class="post-author"><a href="https://wlizhi.cc" title="Author" target="_blank" rel="noopener noreffer author" class="author"><i class="fas fa-user-circle fa-fw"></i>Wlizhi</a></span>&nbsp;<span class="post-category">收录于 <a href="/categories/%E7%BD%91%E7%BB%9C/"><i class="far fa-folder fa-fw"></i>网络</a></span></div>
            <div class="post-meta-line"><i class="far fa-calendar-alt fa-fw"></i>&nbsp;<time datetime="2024-05-26">2024-05-26</time>&nbsp;<i class="fas fa-pencil-alt fa-fw"></i>&nbsp;约 11950 字&nbsp;
                <i class="far fa-clock fa-fw"></i>&nbsp;预计阅读 24 分钟&nbsp;<span id="/posts/network/netty01-nio/" class="leancloud_visitors" data-flag-title="JAVA NIO">
                        <i class="far fa-eye fa-fw"></i>&nbsp;<span class=leancloud-visitors-count></span>&nbsp;次阅读
                    </span>&nbsp;</div>
        </div><div class="details toc" id="toc-static"  kept="">
                <div class="details-summary toc-title">
                    <span>目录</span>
                    <span><i class="details-icon fas fa-angle-right"></i></span>
                </div>
                <div class="details-content toc-content" id="toc-content-static"><nav id="TableOfContents">
  <ul>
    <li><a href="#1-三大组件">1. 三大组件</a>
      <ul>
        <li><a href="#11-channel--buffer">1.1 Channel &amp; Buffer</a></li>
        <li><a href="#12-selector">1.2 Selector</a>
          <ul>
            <li><a href="#多线程版设计">多线程版设计</a></li>
            <li><a href="#-多线程版缺点">⚠️ 多线程版缺点</a></li>
            <li><a href="#线程池版设计">线程池版设计</a></li>
            <li><a href="#-线程池版缺点">⚠️ 线程池版缺点</a></li>
            <li><a href="#selector-版设计">selector 版设计</a></li>
          </ul>
        </li>
      </ul>
    </li>
    <li><a href="#2-bytebuffer">2. ByteBuffer</a>
      <ul>
        <li><a href="#21--bytebuffer-正确使用姿势">2.1  ByteBuffer 正确使用姿势</a></li>
        <li><a href="#22-bytebuffer-结构">2.2 ByteBuffer 结构</a>
          <ul>
            <li><a href="#-调试工具类">💡 调试工具类</a></li>
          </ul>
        </li>
        <li><a href="#23-bytebuffer-常见方法">2.3 ByteBuffer 常见方法</a>
          <ul>
            <li><a href="#分配空间">分配空间</a></li>
            <li><a href="#向-buffer-写入数据">向 buffer 写入数据</a></li>
            <li><a href="#从-buffer-读取数据">从 buffer 读取数据</a></li>
            <li><a href="#mark-和-reset">mark 和 reset</a></li>
            <li><a href="#字符串与-bytebuffer-互转">字符串与 ByteBuffer 互转</a></li>
            <li><a href="#-buffer-的线程安全">⚠️ Buffer 的线程安全</a></li>
          </ul>
        </li>
        <li><a href="#24-scattering-reads">2.4 Scattering Reads</a></li>
        <li><a href="#25-gathering-writes">2.5 Gathering Writes</a></li>
        <li><a href="#26-练习">2.6 练习</a></li>
      </ul>
    </li>
    <li><a href="#3-文件编程">3. 文件编程</a>
      <ul>
        <li><a href="#31-filechannel">3.1 FileChannel</a>
          <ul>
            <li><a href="#-filechannel-工作模式">⚠️ FileChannel 工作模式</a></li>
            <li><a href="#获取">获取</a></li>
            <li><a href="#读取">读取</a></li>
            <li><a href="#写入">写入</a></li>
            <li><a href="#关闭">关闭</a></li>
            <li><a href="#位置">位置</a></li>
            <li><a href="#大小">大小</a></li>
            <li><a href="#强制写入">强制写入</a></li>
          </ul>
        </li>
        <li><a href="#32-两个-channel-传输数据">3.2 两个 Channel 传输数据</a></li>
        <li><a href="#33-path">3.3 Path</a></li>
        <li><a href="#34-files">3.4 Files</a>
          <ul>
            <li><a href="#-删除很危险">⚠️ 删除很危险</a></li>
          </ul>
        </li>
      </ul>
    </li>
    <li><a href="#4-网络编程">4. 网络编程</a>
      <ul>
        <li><a href="#41-非阻塞-vs-阻塞">4.1 非阻塞 vs 阻塞</a>
          <ul>
            <li><a href="#阻塞">阻塞</a></li>
            <li><a href="#非阻塞">非阻塞</a></li>
            <li><a href="#多路复用">多路复用</a></li>
          </ul>
        </li>
        <li><a href="#42-selector">4.2 Selector</a>
          <ul>
            <li><a href="#创建">创建</a></li>
            <li><a href="#绑定-channel-事件">绑定 Channel 事件</a></li>
            <li><a href="#监听-channel-事件">监听 Channel 事件</a></li>
            <li><a href="#-select-何时不阻塞">💡 select 何时不阻塞</a></li>
          </ul>
        </li>
        <li><a href="#43-处理-accept-事件">4.3 处理 accept 事件</a>
          <ul>
            <li><a href="#-事件发生后能否不处理">💡 事件发生后能否不处理</a></li>
          </ul>
        </li>
        <li><a href="#44-处理-read-事件">4.4 处理 read 事件</a>
          <ul>
            <li><a href="#-为何要-iterremove">💡 为何要 iter.remove()</a></li>
            <li><a href="#-cancel-的作用">💡 cancel 的作用</a></li>
            <li><a href="#--不处理边界的问题">⚠️  不处理边界的问题</a></li>
            <li><a href="#处理消息的边界">处理消息的边界</a></li>
            <li><a href="#bytebuffer-大小分配">ByteBuffer 大小分配</a></li>
          </ul>
        </li>
        <li><a href="#45-处理-write-事件">4.5 处理 write 事件</a>
          <ul>
            <li><a href="#一次无法写完例子">一次无法写完例子</a></li>
            <li><a href="#-write-为何要取消">💡 write 为何要取消</a></li>
          </ul>
        </li>
        <li><a href="#46-更进一步">4.6 更进一步</a>
          <ul>
            <li><a href="#-利用多线程优化">💡 利用多线程优化</a></li>
            <li><a href="#-如何拿到-cpu-个数">💡 如何拿到 cpu 个数</a></li>
          </ul>
        </li>
        <li><a href="#47-udp">4.7 UDP</a></li>
      </ul>
    </li>
    <li><a href="#5-nio-vs-bio">5. NIO vs BIO</a>
      <ul>
        <li><a href="#51-stream-vs-channel">5.1 stream vs channel</a></li>
        <li><a href="#52-io-模型">5.2 IO 模型</a>
          <ul>
            <li><a href="#-参考">🔖 参考</a></li>
          </ul>
        </li>
        <li><a href="#53-零拷贝">5.3 零拷贝</a>
          <ul>
            <li><a href="#传统-io-问题">传统 IO 问题</a></li>
            <li><a href="#nio-优化">NIO 优化</a></li>
          </ul>
        </li>
        <li><a href="#53-aio">5.3 AIO</a>
          <ul>
            <li><a href="#文件-aio">文件 AIO</a></li>
            <li><a href="#-守护线程">💡 守护线程</a></li>
            <li><a href="#网络-aio">网络 AIO</a></li>
          </ul>
        </li>
      </ul>
    </li>
  </ul>
</nav></div>
            </div><div class="content" id="content"><p>Java NIO 基础知识</p>
<h1 id="一-nio-基础">一. NIO 基础</h1>
<p>non-blocking io 非阻塞 IO</p>
<h2 id="1-三大组件">1. 三大组件</h2>
<h3 id="11-channel--buffer">1.1 Channel &amp; Buffer</h3>
<p>channel 有一点类似于 stream，它就是读写数据的<strong>双向通道</strong>，可以从 channel 将数据读入 buffer，也可以将 buffer 的数据写入 channel，而之前的 stream 要么是输入，要么是输出，channel 比 stream 更为底层</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span><span class="lnt">2
</span><span class="lnt">3
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-fallback" data-lang="fallback"><span class="line"><span class="cl">graph LR
</span></span><span class="line"><span class="cl">channel --&gt; buffer
</span></span><span class="line"><span class="cl">buffer --&gt; channel
</span></span></code></pre></td></tr></table>
</div>
</div><p>常见的 Channel 有</p>
<ul>
<li>FileChannel</li>
<li>DatagramChannel</li>
<li>SocketChannel</li>
<li>ServerSocketChannel</li>
</ul>
<p>buffer 则用来缓冲读写数据，常见的 buffer 有</p>
<ul>
<li>ByteBuffer
<ul>
<li>MappedByteBuffer</li>
<li>DirectByteBuffer</li>
<li>HeapByteBuffer</li>
</ul>
</li>
<li>ShortBuffer</li>
<li>IntBuffer</li>
<li>LongBuffer</li>
<li>FloatBuffer</li>
<li>DoubleBuffer</li>
<li>CharBuffer</li>
</ul>
<h3 id="12-selector">1.2 Selector</h3>
<p>selector 单从字面意思不好理解，需要结合服务器的设计演化来理解它的用途</p>
<h4 id="多线程版设计">多线程版设计</h4>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span><span class="lnt">2
</span><span class="lnt">3
</span><span class="lnt">4
</span><span class="lnt">5
</span><span class="lnt">6
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-fallback" data-lang="fallback"><span class="line"><span class="cl">graph TD
</span></span><span class="line"><span class="cl">subgraph 多线程版
</span></span><span class="line"><span class="cl">t1(thread) --&gt; s1(socket1)
</span></span><span class="line"><span class="cl">t2(thread) --&gt; s2(socket2)
</span></span><span class="line"><span class="cl">t3(thread) --&gt; s3(socket3)
</span></span><span class="line"><span class="cl">end
</span></span></code></pre></td></tr></table>
</div>
</div><h4 id="-多线程版缺点">⚠️ 多线程版缺点</h4>
<ul>
<li>内存占用高</li>
<li>线程上下文切换成本高</li>
<li>只适合连接数少的场景</li>
</ul>
<h4 id="线程池版设计">线程池版设计</h4>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span><span class="lnt">2
</span><span class="lnt">3
</span><span class="lnt">4
</span><span class="lnt">5
</span><span class="lnt">6
</span><span class="lnt">7
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-fallback" data-lang="fallback"><span class="line"><span class="cl">graph TD
</span></span><span class="line"><span class="cl">subgraph 线程池版
</span></span><span class="line"><span class="cl">t4(thread) --&gt; s4(socket1)
</span></span><span class="line"><span class="cl">t5(thread) --&gt; s5(socket2)
</span></span><span class="line"><span class="cl">t4(thread) -.-&gt; s6(socket3)
</span></span><span class="line"><span class="cl">t5(thread) -.-&gt; s7(socket4)
</span></span><span class="line"><span class="cl">end
</span></span></code></pre></td></tr></table>
</div>
</div><h4 id="-线程池版缺点">⚠️ 线程池版缺点</h4>
<ul>
<li>阻塞模式下，线程仅能处理一个 socket 连接</li>
<li>仅适合短连接场景</li>
</ul>
<h4 id="selector-版设计">selector 版设计</h4>
<p>selector 的作用就是配合一个线程来管理多个 channel，获取这些 channel 上发生的事件，这些 channel 工作在非阻塞模式下，不会让线程吊死在一个 channel 上。适合连接数特别多，但流量低的场景（low traffic）</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span><span class="lnt">2
</span><span class="lnt">3
</span><span class="lnt">4
</span><span class="lnt">5
</span><span class="lnt">6
</span><span class="lnt">7
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-fallback" data-lang="fallback"><span class="line"><span class="cl">graph TD
</span></span><span class="line"><span class="cl">subgraph selector 版
</span></span><span class="line"><span class="cl">thread --&gt; selector
</span></span><span class="line"><span class="cl">selector --&gt; c1(channel)
</span></span><span class="line"><span class="cl">selector --&gt; c2(channel)
</span></span><span class="line"><span class="cl">selector --&gt; c3(channel)
</span></span><span class="line"><span class="cl">end
</span></span></code></pre></td></tr></table>
</div>
</div><p>调用 selector 的 select() 会阻塞直到 channel 发生了读写就绪事件，这些事件发生，select 方法就会返回这些事件交给 thread 来处理</p>
<h2 id="2-bytebuffer">2. ByteBuffer</h2>
<p>有一普通文本文件 data.txt，内容为</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-fallback" data-lang="fallback"><span class="line"><span class="cl">1234567890abcd
</span></span></code></pre></td></tr></table>
</div>
</div><p>使用 FileChannel 来读取文件内容</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt"> 1
</span><span class="lnt"> 2
</span><span class="lnt"> 3
</span><span class="lnt"> 4
</span><span class="lnt"> 5
</span><span class="lnt"> 6
</span><span class="lnt"> 7
</span><span class="lnt"> 8
</span><span class="lnt"> 9
</span><span class="lnt">10
</span><span class="lnt">11
</span><span class="lnt">12
</span><span class="lnt">13
</span><span class="lnt">14
</span><span class="lnt">15
</span><span class="lnt">16
</span><span class="lnt">17
</span><span class="lnt">18
</span><span class="lnt">19
</span><span class="lnt">20
</span><span class="lnt">21
</span><span class="lnt">22
</span><span class="lnt">23
</span><span class="lnt">24
</span><span class="lnt">25
</span><span class="lnt">26
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-java" data-lang="java"><span class="line"><span class="cl"><span class="nd">@Slf4j</span>
</span></span><span class="line"><span class="cl"><span class="kd">public</span> <span class="kd">class</span> <span class="nc">ChannelDemo1</span> <span class="o">{</span>
</span></span><span class="line"><span class="cl">    <span class="kd">public</span> <span class="kd">static</span> <span class="kt">void</span> <span class="nf">main</span><span class="o">(</span><span class="n">String</span><span class="o">[]</span> <span class="n">args</span><span class="o">)</span> <span class="o">{</span>
</span></span><span class="line"><span class="cl">        <span class="k">try</span> <span class="o">(</span><span class="n">RandomAccessFile</span> <span class="n">file</span> <span class="o">=</span> <span class="k">new</span> <span class="n">RandomAccessFile</span><span class="o">(</span><span class="s">&#34;helloword/data.txt&#34;</span><span class="o">,</span> <span class="s">&#34;rw&#34;</span><span class="o">))</span> <span class="o">{</span>
</span></span><span class="line"><span class="cl">            <span class="n">FileChannel</span> <span class="n">channel</span> <span class="o">=</span> <span class="n">file</span><span class="o">.</span><span class="na">getChannel</span><span class="o">();</span>
</span></span><span class="line"><span class="cl">            <span class="n">ByteBuffer</span> <span class="n">buffer</span> <span class="o">=</span> <span class="n">ByteBuffer</span><span class="o">.</span><span class="na">allocate</span><span class="o">(</span><span class="n">10</span><span class="o">);</span>
</span></span><span class="line"><span class="cl">            <span class="k">do</span> <span class="o">{</span>
</span></span><span class="line"><span class="cl">                <span class="c1">// 向 buffer 写入
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>                <span class="kt">int</span> <span class="n">len</span> <span class="o">=</span> <span class="n">channel</span><span class="o">.</span><span class="na">read</span><span class="o">(</span><span class="n">buffer</span><span class="o">);</span>
</span></span><span class="line"><span class="cl">                <span class="n">log</span><span class="o">.</span><span class="na">debug</span><span class="o">(</span><span class="s">&#34;读到字节数：{}&#34;</span><span class="o">,</span> <span class="n">len</span><span class="o">);</span>
</span></span><span class="line"><span class="cl">                <span class="k">if</span> <span class="o">(</span><span class="n">len</span> <span class="o">==</span> <span class="o">-</span><span class="n">1</span><span class="o">)</span> <span class="o">{</span>
</span></span><span class="line"><span class="cl">                    <span class="k">break</span><span class="o">;</span>
</span></span><span class="line"><span class="cl">                <span class="o">}</span>
</span></span><span class="line"><span class="cl">                <span class="c1">// 切换 buffer 读模式
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>                <span class="n">buffer</span><span class="o">.</span><span class="na">flip</span><span class="o">();</span>
</span></span><span class="line"><span class="cl">                <span class="k">while</span><span class="o">(</span><span class="n">buffer</span><span class="o">.</span><span class="na">hasRemaining</span><span class="o">())</span> <span class="o">{</span>
</span></span><span class="line"><span class="cl">                    <span class="n">log</span><span class="o">.</span><span class="na">debug</span><span class="o">(</span><span class="s">&#34;{}&#34;</span><span class="o">,</span> <span class="o">(</span><span class="kt">char</span><span class="o">)</span><span class="n">buffer</span><span class="o">.</span><span class="na">get</span><span class="o">());</span>
</span></span><span class="line"><span class="cl">                <span class="o">}</span>
</span></span><span class="line"><span class="cl">                <span class="c1">// 切换 buffer 写模式
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>                <span class="n">buffer</span><span class="o">.</span><span class="na">clear</span><span class="o">();</span>
</span></span><span class="line"><span class="cl">            <span class="o">}</span> <span class="k">while</span> <span class="o">(</span><span class="kc">true</span><span class="o">);</span>
</span></span><span class="line"><span class="cl">        <span class="o">}</span> <span class="k">catch</span> <span class="o">(</span><span class="n">IOException</span> <span class="n">e</span><span class="o">)</span> <span class="o">{</span>
</span></span><span class="line"><span class="cl">            <span class="n">e</span><span class="o">.</span><span class="na">printStackTrace</span><span class="o">();</span>
</span></span><span class="line"><span class="cl">        <span class="o">}</span>
</span></span><span class="line"><span class="cl">    <span class="o">}</span>
</span></span><span class="line"><span class="cl"><span class="o">}</span>
</span></span></code></pre></td></tr></table>
</div>
</div><p>输出</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt"> 1
</span><span class="lnt"> 2
</span><span class="lnt"> 3
</span><span class="lnt"> 4
</span><span class="lnt"> 5
</span><span class="lnt"> 6
</span><span class="lnt"> 7
</span><span class="lnt"> 8
</span><span class="lnt"> 9
</span><span class="lnt">10
</span><span class="lnt">11
</span><span class="lnt">12
</span><span class="lnt">13
</span><span class="lnt">14
</span><span class="lnt">15
</span><span class="lnt">16
</span><span class="lnt">17
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-fallback" data-lang="fallback"><span class="line"><span class="cl">10:39:03 [DEBUG] [main] c.i.n.ChannelDemo1 - 读到字节数：10
</span></span><span class="line"><span class="cl">10:39:03 [DEBUG] [main] c.i.n.ChannelDemo1 - 1
</span></span><span class="line"><span class="cl">10:39:03 [DEBUG] [main] c.i.n.ChannelDemo1 - 2
</span></span><span class="line"><span class="cl">10:39:03 [DEBUG] [main] c.i.n.ChannelDemo1 - 3
</span></span><span class="line"><span class="cl">10:39:03 [DEBUG] [main] c.i.n.ChannelDemo1 - 4
</span></span><span class="line"><span class="cl">10:39:03 [DEBUG] [main] c.i.n.ChannelDemo1 - 5
</span></span><span class="line"><span class="cl">10:39:03 [DEBUG] [main] c.i.n.ChannelDemo1 - 6
</span></span><span class="line"><span class="cl">10:39:03 [DEBUG] [main] c.i.n.ChannelDemo1 - 7
</span></span><span class="line"><span class="cl">10:39:03 [DEBUG] [main] c.i.n.ChannelDemo1 - 8
</span></span><span class="line"><span class="cl">10:39:03 [DEBUG] [main] c.i.n.ChannelDemo1 - 9
</span></span><span class="line"><span class="cl">10:39:03 [DEBUG] [main] c.i.n.ChannelDemo1 - 0
</span></span><span class="line"><span class="cl">10:39:03 [DEBUG] [main] c.i.n.ChannelDemo1 - 读到字节数：4
</span></span><span class="line"><span class="cl">10:39:03 [DEBUG] [main] c.i.n.ChannelDemo1 - a
</span></span><span class="line"><span class="cl">10:39:03 [DEBUG] [main] c.i.n.ChannelDemo1 - b
</span></span><span class="line"><span class="cl">10:39:03 [DEBUG] [main] c.i.n.ChannelDemo1 - c
</span></span><span class="line"><span class="cl">10:39:03 [DEBUG] [main] c.i.n.ChannelDemo1 - d
</span></span><span class="line"><span class="cl">10:39:03 [DEBUG] [main] c.i.n.ChannelDemo1 - 读到字节数：-1
</span></span></code></pre></td></tr></table>
</div>
</div><h3 id="21--bytebuffer-正确使用姿势">2.1  ByteBuffer 正确使用姿势</h3>
<ol>
<li>向 buffer 写入数据，例如调用 channel.read(buffer)</li>
<li>调用 flip() 切换至<strong>读模式</strong></li>
<li>从 buffer 读取数据，例如调用 buffer.get()</li>
<li>调用 clear() 或 compact() 切换至<strong>写模式</strong></li>
<li>重复 1~4 步骤</li>
</ol>
<h3 id="22-bytebuffer-结构">2.2 ByteBuffer 结构</h3>
<p>ByteBuffer 有以下重要属性</p>
<ul>
<li>capacity</li>
<li>position</li>
<li>limit</li>
</ul>
<p>一开始</p>
<p><img
        class="lazyload"
        src="/svg/loading.min.svg"
        data-src="/img/0021.png"
        data-srcset="/img/0021.png, /img/0021.png 1.5x, /img/0021.png 2x"
        data-sizes="auto"
        alt="/img/0021.png"
        title="/img/0021.png" /></p>
<p>写模式下，position 是写入位置，limit 等于容量，下图表示写入了 4 个字节后的状态</p>
<p><img
        class="lazyload"
        src="/svg/loading.min.svg"
        data-src="/img/0018.png"
        data-srcset="/img/0018.png, /img/0018.png 1.5x, /img/0018.png 2x"
        data-sizes="auto"
        alt="/img/0018.png"
        title="/img/0018.png" /></p>
<p>flip 动作发生后，position 切换为读取位置，limit 切换为读取限制</p>
<p><img
        class="lazyload"
        src="/svg/loading.min.svg"
        data-src="/img/0019.png"
        data-srcset="/img/0019.png, /img/0019.png 1.5x, /img/0019.png 2x"
        data-sizes="auto"
        alt="/img/0019.png"
        title="/img/0019.png" /></p>
<p>读取 4 个字节后，状态</p>
<p><img
        class="lazyload"
        src="/svg/loading.min.svg"
        data-src="/img/0020.png"
        data-srcset="/img/0020.png, /img/0020.png 1.5x, /img/0020.png 2x"
        data-sizes="auto"
        alt="/img/0020.png"
        title="/img/0020.png" /></p>
<p>clear 动作发生后，状态</p>
<p><img
        class="lazyload"
        src="/svg/loading.min.svg"
        data-src="/img/0021.png"
        data-srcset="/img/0021.png, /img/0021.png 1.5x, /img/0021.png 2x"
        data-sizes="auto"
        alt="/img/0021.png"
        title="/img/0021.png" /></p>
<p>compact 方法，是把未读完的部分向前压缩，然后切换至写模式</p>
<p><img
        class="lazyload"
        src="/svg/loading.min.svg"
        data-src="/img/0022.png"
        data-srcset="/img/0022.png, /img/0022.png 1.5x, /img/0022.png 2x"
        data-sizes="auto"
        alt="/img/0022.png"
        title="/img/0022.png" /></p>
<h4 id="-调试工具类">💡 调试工具类</h4>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">  1
</span><span class="lnt">  2
</span><span class="lnt">  3
</span><span class="lnt">  4
</span><span class="lnt">  5
</span><span class="lnt">  6
</span><span class="lnt">  7
</span><span class="lnt">  8
</span><span class="lnt">  9
</span><span class="lnt"> 10
</span><span class="lnt"> 11
</span><span class="lnt"> 12
</span><span class="lnt"> 13
</span><span class="lnt"> 14
</span><span class="lnt"> 15
</span><span class="lnt"> 16
</span><span class="lnt"> 17
</span><span class="lnt"> 18
</span><span class="lnt"> 19
</span><span class="lnt"> 20
</span><span class="lnt"> 21
</span><span class="lnt"> 22
</span><span class="lnt"> 23
</span><span class="lnt"> 24
</span><span class="lnt"> 25
</span><span class="lnt"> 26
</span><span class="lnt"> 27
</span><span class="lnt"> 28
</span><span class="lnt"> 29
</span><span class="lnt"> 30
</span><span class="lnt"> 31
</span><span class="lnt"> 32
</span><span class="lnt"> 33
</span><span class="lnt"> 34
</span><span class="lnt"> 35
</span><span class="lnt"> 36
</span><span class="lnt"> 37
</span><span class="lnt"> 38
</span><span class="lnt"> 39
</span><span class="lnt"> 40
</span><span class="lnt"> 41
</span><span class="lnt"> 42
</span><span class="lnt"> 43
</span><span class="lnt"> 44
</span><span class="lnt"> 45
</span><span class="lnt"> 46
</span><span class="lnt"> 47
</span><span class="lnt"> 48
</span><span class="lnt"> 49
</span><span class="lnt"> 50
</span><span class="lnt"> 51
</span><span class="lnt"> 52
</span><span class="lnt"> 53
</span><span class="lnt"> 54
</span><span class="lnt"> 55
</span><span class="lnt"> 56
</span><span class="lnt"> 57
</span><span class="lnt"> 58
</span><span class="lnt"> 59
</span><span class="lnt"> 60
</span><span class="lnt"> 61
</span><span class="lnt"> 62
</span><span class="lnt"> 63
</span><span class="lnt"> 64
</span><span class="lnt"> 65
</span><span class="lnt"> 66
</span><span class="lnt"> 67
</span><span class="lnt"> 68
</span><span class="lnt"> 69
</span><span class="lnt"> 70
</span><span class="lnt"> 71
</span><span class="lnt"> 72
</span><span class="lnt"> 73
</span><span class="lnt"> 74
</span><span class="lnt"> 75
</span><span class="lnt"> 76
</span><span class="lnt"> 77
</span><span class="lnt"> 78
</span><span class="lnt"> 79
</span><span class="lnt"> 80
</span><span class="lnt"> 81
</span><span class="lnt"> 82
</span><span class="lnt"> 83
</span><span class="lnt"> 84
</span><span class="lnt"> 85
</span><span class="lnt"> 86
</span><span class="lnt"> 87
</span><span class="lnt"> 88
</span><span class="lnt"> 89
</span><span class="lnt"> 90
</span><span class="lnt"> 91
</span><span class="lnt"> 92
</span><span class="lnt"> 93
</span><span class="lnt"> 94
</span><span class="lnt"> 95
</span><span class="lnt"> 96
</span><span class="lnt"> 97
</span><span class="lnt"> 98
</span><span class="lnt"> 99
</span><span class="lnt">100
</span><span class="lnt">101
</span><span class="lnt">102
</span><span class="lnt">103
</span><span class="lnt">104
</span><span class="lnt">105
</span><span class="lnt">106
</span><span class="lnt">107
</span><span class="lnt">108
</span><span class="lnt">109
</span><span class="lnt">110
</span><span class="lnt">111
</span><span class="lnt">112
</span><span class="lnt">113
</span><span class="lnt">114
</span><span class="lnt">115
</span><span class="lnt">116
</span><span class="lnt">117
</span><span class="lnt">118
</span><span class="lnt">119
</span><span class="lnt">120
</span><span class="lnt">121
</span><span class="lnt">122
</span><span class="lnt">123
</span><span class="lnt">124
</span><span class="lnt">125
</span><span class="lnt">126
</span><span class="lnt">127
</span><span class="lnt">128
</span><span class="lnt">129
</span><span class="lnt">130
</span><span class="lnt">131
</span><span class="lnt">132
</span><span class="lnt">133
</span><span class="lnt">134
</span><span class="lnt">135
</span><span class="lnt">136
</span><span class="lnt">137
</span><span class="lnt">138
</span><span class="lnt">139
</span><span class="lnt">140
</span><span class="lnt">141
</span><span class="lnt">142
</span><span class="lnt">143
</span><span class="lnt">144
</span><span class="lnt">145
</span><span class="lnt">146
</span><span class="lnt">147
</span><span class="lnt">148
</span><span class="lnt">149
</span><span class="lnt">150
</span><span class="lnt">151
</span><span class="lnt">152
</span><span class="lnt">153
</span><span class="lnt">154
</span><span class="lnt">155
</span><span class="lnt">156
</span><span class="lnt">157
</span><span class="lnt">158
</span><span class="lnt">159
</span><span class="lnt">160
</span><span class="lnt">161
</span><span class="lnt">162
</span><span class="lnt">163
</span><span class="lnt">164
</span><span class="lnt">165
</span><span class="lnt">166
</span><span class="lnt">167
</span><span class="lnt">168
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-java" data-lang="java"><span class="line"><span class="cl"><span class="kd">public</span> <span class="kd">class</span> <span class="nc">ByteBufferUtil</span> <span class="o">{</span>
</span></span><span class="line"><span class="cl">    <span class="kd">private</span> <span class="kd">static</span> <span class="kd">final</span> <span class="kt">char</span><span class="o">[]</span> <span class="n">BYTE2CHAR</span> <span class="o">=</span> <span class="k">new</span> <span class="kt">char</span><span class="o">[</span><span class="n">256</span><span class="o">];</span>
</span></span><span class="line"><span class="cl">    <span class="kd">private</span> <span class="kd">static</span> <span class="kd">final</span> <span class="kt">char</span><span class="o">[]</span> <span class="n">HEXDUMP_TABLE</span> <span class="o">=</span> <span class="k">new</span> <span class="kt">char</span><span class="o">[</span><span class="n">256</span> <span class="o">*</span> <span class="n">4</span><span class="o">];</span>
</span></span><span class="line"><span class="cl">    <span class="kd">private</span> <span class="kd">static</span> <span class="kd">final</span> <span class="n">String</span><span class="o">[]</span> <span class="n">HEXPADDING</span> <span class="o">=</span> <span class="k">new</span> <span class="n">String</span><span class="o">[</span><span class="n">16</span><span class="o">];</span>
</span></span><span class="line"><span class="cl">    <span class="kd">private</span> <span class="kd">static</span> <span class="kd">final</span> <span class="n">String</span><span class="o">[]</span> <span class="n">HEXDUMP_ROWPREFIXES</span> <span class="o">=</span> <span class="k">new</span> <span class="n">String</span><span class="o">[</span><span class="n">65536</span> <span class="o">&gt;&gt;&gt;</span> <span class="n">4</span><span class="o">];</span>
</span></span><span class="line"><span class="cl">    <span class="kd">private</span> <span class="kd">static</span> <span class="kd">final</span> <span class="n">String</span><span class="o">[]</span> <span class="n">BYTE2HEX</span> <span class="o">=</span> <span class="k">new</span> <span class="n">String</span><span class="o">[</span><span class="n">256</span><span class="o">];</span>
</span></span><span class="line"><span class="cl">    <span class="kd">private</span> <span class="kd">static</span> <span class="kd">final</span> <span class="n">String</span><span class="o">[]</span> <span class="n">BYTEPADDING</span> <span class="o">=</span> <span class="k">new</span> <span class="n">String</span><span class="o">[</span><span class="n">16</span><span class="o">];</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="kd">static</span> <span class="o">{</span>
</span></span><span class="line"><span class="cl">        <span class="kd">final</span> <span class="kt">char</span><span class="o">[]</span> <span class="n">DIGITS</span> <span class="o">=</span> <span class="s">&#34;0123456789abcdef&#34;</span><span class="o">.</span><span class="na">toCharArray</span><span class="o">();</span>
</span></span><span class="line"><span class="cl">        <span class="k">for</span> <span class="o">(</span><span class="kt">int</span> <span class="n">i</span> <span class="o">=</span> <span class="n">0</span><span class="o">;</span> <span class="n">i</span> <span class="o">&lt;</span> <span class="n">256</span><span class="o">;</span> <span class="n">i</span><span class="o">++)</span> <span class="o">{</span>
</span></span><span class="line"><span class="cl">            <span class="n">HEXDUMP_TABLE</span><span class="o">[</span><span class="n">i</span> <span class="o">&lt;&lt;</span> <span class="n">1</span><span class="o">]</span> <span class="o">=</span> <span class="n">DIGITS</span><span class="o">[</span><span class="n">i</span> <span class="o">&gt;&gt;&gt;</span> <span class="n">4</span> <span class="o">&amp;</span> <span class="n">0x0F</span><span class="o">];</span>
</span></span><span class="line"><span class="cl">            <span class="n">HEXDUMP_TABLE</span><span class="o">[(</span><span class="n">i</span> <span class="o">&lt;&lt;</span> <span class="n">1</span><span class="o">)</span> <span class="o">+</span> <span class="n">1</span><span class="o">]</span> <span class="o">=</span> <span class="n">DIGITS</span><span class="o">[</span><span class="n">i</span> <span class="o">&amp;</span> <span class="n">0x0F</span><span class="o">];</span>
</span></span><span class="line"><span class="cl">        <span class="o">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">        <span class="kt">int</span> <span class="n">i</span><span class="o">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">        <span class="c1">// Generate the lookup table for hex dump paddings
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>        <span class="k">for</span> <span class="o">(</span><span class="n">i</span> <span class="o">=</span> <span class="n">0</span><span class="o">;</span> <span class="n">i</span> <span class="o">&lt;</span> <span class="n">HEXPADDING</span><span class="o">.</span><span class="na">length</span><span class="o">;</span> <span class="n">i</span><span class="o">++)</span> <span class="o">{</span>
</span></span><span class="line"><span class="cl">            <span class="kt">int</span> <span class="n">padding</span> <span class="o">=</span> <span class="n">HEXPADDING</span><span class="o">.</span><span class="na">length</span> <span class="o">-</span> <span class="n">i</span><span class="o">;</span>
</span></span><span class="line"><span class="cl">            <span class="n">StringBuilder</span> <span class="n">buf</span> <span class="o">=</span> <span class="k">new</span> <span class="n">StringBuilder</span><span class="o">(</span><span class="n">padding</span> <span class="o">*</span> <span class="n">3</span><span class="o">);</span>
</span></span><span class="line"><span class="cl">            <span class="k">for</span> <span class="o">(</span><span class="kt">int</span> <span class="n">j</span> <span class="o">=</span> <span class="n">0</span><span class="o">;</span> <span class="n">j</span> <span class="o">&lt;</span> <span class="n">padding</span><span class="o">;</span> <span class="n">j</span><span class="o">++)</span> <span class="o">{</span>
</span></span><span class="line"><span class="cl">                <span class="n">buf</span><span class="o">.</span><span class="na">append</span><span class="o">(</span><span class="s">&#34;   &#34;</span><span class="o">);</span>
</span></span><span class="line"><span class="cl">            <span class="o">}</span>
</span></span><span class="line"><span class="cl">            <span class="n">HEXPADDING</span><span class="o">[</span><span class="n">i</span><span class="o">]</span> <span class="o">=</span> <span class="n">buf</span><span class="o">.</span><span class="na">toString</span><span class="o">();</span>
</span></span><span class="line"><span class="cl">        <span class="o">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">        <span class="c1">// Generate the lookup table for the start-offset header in each row (up to 64KiB).
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>        <span class="k">for</span> <span class="o">(</span><span class="n">i</span> <span class="o">=</span> <span class="n">0</span><span class="o">;</span> <span class="n">i</span> <span class="o">&lt;</span> <span class="n">HEXDUMP_ROWPREFIXES</span><span class="o">.</span><span class="na">length</span><span class="o">;</span> <span class="n">i</span><span class="o">++)</span> <span class="o">{</span>
</span></span><span class="line"><span class="cl">            <span class="n">StringBuilder</span> <span class="n">buf</span> <span class="o">=</span> <span class="k">new</span> <span class="n">StringBuilder</span><span class="o">(</span><span class="n">12</span><span class="o">);</span>
</span></span><span class="line"><span class="cl">            <span class="n">buf</span><span class="o">.</span><span class="na">append</span><span class="o">(</span><span class="n">NEWLINE</span><span class="o">);</span>
</span></span><span class="line"><span class="cl">            <span class="n">buf</span><span class="o">.</span><span class="na">append</span><span class="o">(</span><span class="n">Long</span><span class="o">.</span><span class="na">toHexString</span><span class="o">(</span><span class="n">i</span> <span class="o">&lt;&lt;</span> <span class="n">4</span> <span class="o">&amp;</span> <span class="n">0xFFFFFFFFL</span> <span class="o">|</span> <span class="n">0x100000000L</span><span class="o">));</span>
</span></span><span class="line"><span class="cl">            <span class="n">buf</span><span class="o">.</span><span class="na">setCharAt</span><span class="o">(</span><span class="n">buf</span><span class="o">.</span><span class="na">length</span><span class="o">()</span> <span class="o">-</span> <span class="n">9</span><span class="o">,</span> <span class="sc">&#39;|&#39;</span><span class="o">);</span>
</span></span><span class="line"><span class="cl">            <span class="n">buf</span><span class="o">.</span><span class="na">append</span><span class="o">(</span><span class="sc">&#39;|&#39;</span><span class="o">);</span>
</span></span><span class="line"><span class="cl">            <span class="n">HEXDUMP_ROWPREFIXES</span><span class="o">[</span><span class="n">i</span><span class="o">]</span> <span class="o">=</span> <span class="n">buf</span><span class="o">.</span><span class="na">toString</span><span class="o">();</span>
</span></span><span class="line"><span class="cl">        <span class="o">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">        <span class="c1">// Generate the lookup table for byte-to-hex-dump conversion
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>        <span class="k">for</span> <span class="o">(</span><span class="n">i</span> <span class="o">=</span> <span class="n">0</span><span class="o">;</span> <span class="n">i</span> <span class="o">&lt;</span> <span class="n">BYTE2HEX</span><span class="o">.</span><span class="na">length</span><span class="o">;</span> <span class="n">i</span><span class="o">++)</span> <span class="o">{</span>
</span></span><span class="line"><span class="cl">            <span class="n">BYTE2HEX</span><span class="o">[</span><span class="n">i</span><span class="o">]</span> <span class="o">=</span> <span class="sc">&#39; &#39;</span> <span class="o">+</span> <span class="n">StringUtil</span><span class="o">.</span><span class="na">byteToHexStringPadded</span><span class="o">(</span><span class="n">i</span><span class="o">);</span>
</span></span><span class="line"><span class="cl">        <span class="o">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">        <span class="c1">// Generate the lookup table for byte dump paddings
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>        <span class="k">for</span> <span class="o">(</span><span class="n">i</span> <span class="o">=</span> <span class="n">0</span><span class="o">;</span> <span class="n">i</span> <span class="o">&lt;</span> <span class="n">BYTEPADDING</span><span class="o">.</span><span class="na">length</span><span class="o">;</span> <span class="n">i</span><span class="o">++)</span> <span class="o">{</span>
</span></span><span class="line"><span class="cl">            <span class="kt">int</span> <span class="n">padding</span> <span class="o">=</span> <span class="n">BYTEPADDING</span><span class="o">.</span><span class="na">length</span> <span class="o">-</span> <span class="n">i</span><span class="o">;</span>
</span></span><span class="line"><span class="cl">            <span class="n">StringBuilder</span> <span class="n">buf</span> <span class="o">=</span> <span class="k">new</span> <span class="n">StringBuilder</span><span class="o">(</span><span class="n">padding</span><span class="o">);</span>
</span></span><span class="line"><span class="cl">            <span class="k">for</span> <span class="o">(</span><span class="kt">int</span> <span class="n">j</span> <span class="o">=</span> <span class="n">0</span><span class="o">;</span> <span class="n">j</span> <span class="o">&lt;</span> <span class="n">padding</span><span class="o">;</span> <span class="n">j</span><span class="o">++)</span> <span class="o">{</span>
</span></span><span class="line"><span class="cl">                <span class="n">buf</span><span class="o">.</span><span class="na">append</span><span class="o">(</span><span class="sc">&#39; &#39;</span><span class="o">);</span>
</span></span><span class="line"><span class="cl">            <span class="o">}</span>
</span></span><span class="line"><span class="cl">            <span class="n">BYTEPADDING</span><span class="o">[</span><span class="n">i</span><span class="o">]</span> <span class="o">=</span> <span class="n">buf</span><span class="o">.</span><span class="na">toString</span><span class="o">();</span>
</span></span><span class="line"><span class="cl">        <span class="o">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">        <span class="c1">// Generate the lookup table for byte-to-char conversion
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>        <span class="k">for</span> <span class="o">(</span><span class="n">i</span> <span class="o">=</span> <span class="n">0</span><span class="o">;</span> <span class="n">i</span> <span class="o">&lt;</span> <span class="n">BYTE2CHAR</span><span class="o">.</span><span class="na">length</span><span class="o">;</span> <span class="n">i</span><span class="o">++)</span> <span class="o">{</span>
</span></span><span class="line"><span class="cl">            <span class="k">if</span> <span class="o">(</span><span class="n">i</span> <span class="o">&lt;=</span> <span class="n">0x1f</span> <span class="o">||</span> <span class="n">i</span> <span class="o">&gt;=</span> <span class="n">0x7f</span><span class="o">)</span> <span class="o">{</span>
</span></span><span class="line"><span class="cl">                <span class="n">BYTE2CHAR</span><span class="o">[</span><span class="n">i</span><span class="o">]</span> <span class="o">=</span> <span class="sc">&#39;.&#39;</span><span class="o">;</span>
</span></span><span class="line"><span class="cl">            <span class="o">}</span> <span class="k">else</span> <span class="o">{</span>
</span></span><span class="line"><span class="cl">                <span class="n">BYTE2CHAR</span><span class="o">[</span><span class="n">i</span><span class="o">]</span> <span class="o">=</span> <span class="o">(</span><span class="kt">char</span><span class="o">)</span> <span class="n">i</span><span class="o">;</span>
</span></span><span class="line"><span class="cl">            <span class="o">}</span>
</span></span><span class="line"><span class="cl">        <span class="o">}</span>
</span></span><span class="line"><span class="cl">    <span class="o">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="cm">/**
</span></span></span><span class="line"><span class="cl"><span class="cm">     * 打印所有内容
</span></span></span><span class="line"><span class="cl"><span class="cm">     * @param buffer
</span></span></span><span class="line"><span class="cl"><span class="cm">     */</span>
</span></span><span class="line"><span class="cl">    <span class="kd">public</span> <span class="kd">static</span> <span class="kt">void</span> <span class="nf">debugAll</span><span class="o">(</span><span class="n">ByteBuffer</span> <span class="n">buffer</span><span class="o">)</span> <span class="o">{</span>
</span></span><span class="line"><span class="cl">        <span class="kt">int</span> <span class="n">oldlimit</span> <span class="o">=</span> <span class="n">buffer</span><span class="o">.</span><span class="na">limit</span><span class="o">();</span>
</span></span><span class="line"><span class="cl">        <span class="n">buffer</span><span class="o">.</span><span class="na">limit</span><span class="o">(</span><span class="n">buffer</span><span class="o">.</span><span class="na">capacity</span><span class="o">());</span>
</span></span><span class="line"><span class="cl">        <span class="n">StringBuilder</span> <span class="n">origin</span> <span class="o">=</span> <span class="k">new</span> <span class="n">StringBuilder</span><span class="o">(</span><span class="n">256</span><span class="o">);</span>
</span></span><span class="line"><span class="cl">        <span class="n">appendPrettyHexDump</span><span class="o">(</span><span class="n">origin</span><span class="o">,</span> <span class="n">buffer</span><span class="o">,</span> <span class="n">0</span><span class="o">,</span> <span class="n">buffer</span><span class="o">.</span><span class="na">capacity</span><span class="o">());</span>
</span></span><span class="line"><span class="cl">        <span class="n">System</span><span class="o">.</span><span class="na">out</span><span class="o">.</span><span class="na">println</span><span class="o">(</span><span class="s">&#34;+--------+-------------------- all ------------------------+----------------+&#34;</span><span class="o">);</span>
</span></span><span class="line"><span class="cl">        <span class="n">System</span><span class="o">.</span><span class="na">out</span><span class="o">.</span><span class="na">printf</span><span class="o">(</span><span class="s">&#34;position: [%d], limit: [%d]\n&#34;</span><span class="o">,</span> <span class="n">buffer</span><span class="o">.</span><span class="na">position</span><span class="o">(),</span> <span class="n">oldlimit</span><span class="o">);</span>
</span></span><span class="line"><span class="cl">        <span class="n">System</span><span class="o">.</span><span class="na">out</span><span class="o">.</span><span class="na">println</span><span class="o">(</span><span class="n">origin</span><span class="o">);</span>
</span></span><span class="line"><span class="cl">        <span class="n">buffer</span><span class="o">.</span><span class="na">limit</span><span class="o">(</span><span class="n">oldlimit</span><span class="o">);</span>
</span></span><span class="line"><span class="cl">    <span class="o">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="cm">/**
</span></span></span><span class="line"><span class="cl"><span class="cm">     * 打印可读取内容
</span></span></span><span class="line"><span class="cl"><span class="cm">     * @param buffer
</span></span></span><span class="line"><span class="cl"><span class="cm">     */</span>
</span></span><span class="line"><span class="cl">    <span class="kd">public</span> <span class="kd">static</span> <span class="kt">void</span> <span class="nf">debugRead</span><span class="o">(</span><span class="n">ByteBuffer</span> <span class="n">buffer</span><span class="o">)</span> <span class="o">{</span>
</span></span><span class="line"><span class="cl">        <span class="n">StringBuilder</span> <span class="n">builder</span> <span class="o">=</span> <span class="k">new</span> <span class="n">StringBuilder</span><span class="o">(</span><span class="n">256</span><span class="o">);</span>
</span></span><span class="line"><span class="cl">        <span class="n">appendPrettyHexDump</span><span class="o">(</span><span class="n">builder</span><span class="o">,</span> <span class="n">buffer</span><span class="o">,</span> <span class="n">buffer</span><span class="o">.</span><span class="na">position</span><span class="o">(),</span> <span class="n">buffer</span><span class="o">.</span><span class="na">limit</span><span class="o">()</span> <span class="o">-</span> <span class="n">buffer</span><span class="o">.</span><span class="na">position</span><span class="o">());</span>
</span></span><span class="line"><span class="cl">        <span class="n">System</span><span class="o">.</span><span class="na">out</span><span class="o">.</span><span class="na">println</span><span class="o">(</span><span class="s">&#34;+--------+-------------------- read -----------------------+----------------+&#34;</span><span class="o">);</span>
</span></span><span class="line"><span class="cl">        <span class="n">System</span><span class="o">.</span><span class="na">out</span><span class="o">.</span><span class="na">printf</span><span class="o">(</span><span class="s">&#34;position: [%d], limit: [%d]\n&#34;</span><span class="o">,</span> <span class="n">buffer</span><span class="o">.</span><span class="na">position</span><span class="o">(),</span> <span class="n">buffer</span><span class="o">.</span><span class="na">limit</span><span class="o">());</span>
</span></span><span class="line"><span class="cl">        <span class="n">System</span><span class="o">.</span><span class="na">out</span><span class="o">.</span><span class="na">println</span><span class="o">(</span><span class="n">builder</span><span class="o">);</span>
</span></span><span class="line"><span class="cl">    <span class="o">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="kd">private</span> <span class="kd">static</span> <span class="kt">void</span> <span class="nf">appendPrettyHexDump</span><span class="o">(</span><span class="n">StringBuilder</span> <span class="n">dump</span><span class="o">,</span> <span class="n">ByteBuffer</span> <span class="n">buf</span><span class="o">,</span> <span class="kt">int</span> <span class="n">offset</span><span class="o">,</span> <span class="kt">int</span> <span class="n">length</span><span class="o">)</span> <span class="o">{</span>
</span></span><span class="line"><span class="cl">        <span class="k">if</span> <span class="o">(</span><span class="n">isOutOfBounds</span><span class="o">(</span><span class="n">offset</span><span class="o">,</span> <span class="n">length</span><span class="o">,</span> <span class="n">buf</span><span class="o">.</span><span class="na">capacity</span><span class="o">()))</span> <span class="o">{</span>
</span></span><span class="line"><span class="cl">            <span class="k">throw</span> <span class="k">new</span> <span class="n">IndexOutOfBoundsException</span><span class="o">(</span>
</span></span><span class="line"><span class="cl">                    <span class="s">&#34;expected: &#34;</span> <span class="o">+</span> <span class="s">&#34;0 &lt;= offset(&#34;</span> <span class="o">+</span> <span class="n">offset</span> <span class="o">+</span> <span class="s">&#34;) &lt;= offset + length(&#34;</span> <span class="o">+</span> <span class="n">length</span>
</span></span><span class="line"><span class="cl">                            <span class="o">+</span> <span class="s">&#34;) &lt;= &#34;</span> <span class="o">+</span> <span class="s">&#34;buf.capacity(&#34;</span> <span class="o">+</span> <span class="n">buf</span><span class="o">.</span><span class="na">capacity</span><span class="o">()</span> <span class="o">+</span> <span class="sc">&#39;)&#39;</span><span class="o">);</span>
</span></span><span class="line"><span class="cl">        <span class="o">}</span>
</span></span><span class="line"><span class="cl">        <span class="k">if</span> <span class="o">(</span><span class="n">length</span> <span class="o">==</span> <span class="n">0</span><span class="o">)</span> <span class="o">{</span>
</span></span><span class="line"><span class="cl">            <span class="k">return</span><span class="o">;</span>
</span></span><span class="line"><span class="cl">        <span class="o">}</span>
</span></span><span class="line"><span class="cl">        <span class="n">dump</span><span class="o">.</span><span class="na">append</span><span class="o">(</span>
</span></span><span class="line"><span class="cl">                <span class="s">&#34;         +-------------------------------------------------+&#34;</span> <span class="o">+</span>
</span></span><span class="line"><span class="cl">                        <span class="n">NEWLINE</span> <span class="o">+</span> <span class="s">&#34;         |  0  1  2  3  4  5  6  7  8  9  a  b  c  d  e  f |&#34;</span> <span class="o">+</span>
</span></span><span class="line"><span class="cl">                        <span class="n">NEWLINE</span> <span class="o">+</span> <span class="s">&#34;+--------+-------------------------------------------------+----------------+&#34;</span><span class="o">);</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">        <span class="kd">final</span> <span class="kt">int</span> <span class="n">startIndex</span> <span class="o">=</span> <span class="n">offset</span><span class="o">;</span>
</span></span><span class="line"><span class="cl">        <span class="kd">final</span> <span class="kt">int</span> <span class="n">fullRows</span> <span class="o">=</span> <span class="n">length</span> <span class="o">&gt;&gt;&gt;</span> <span class="n">4</span><span class="o">;</span>
</span></span><span class="line"><span class="cl">        <span class="kd">final</span> <span class="kt">int</span> <span class="n">remainder</span> <span class="o">=</span> <span class="n">length</span> <span class="o">&amp;</span> <span class="n">0xF</span><span class="o">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">        <span class="c1">// Dump the rows which have 16 bytes.
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>        <span class="k">for</span> <span class="o">(</span><span class="kt">int</span> <span class="n">row</span> <span class="o">=</span> <span class="n">0</span><span class="o">;</span> <span class="n">row</span> <span class="o">&lt;</span> <span class="n">fullRows</span><span class="o">;</span> <span class="n">row</span><span class="o">++)</span> <span class="o">{</span>
</span></span><span class="line"><span class="cl">            <span class="kt">int</span> <span class="n">rowStartIndex</span> <span class="o">=</span> <span class="o">(</span><span class="n">row</span> <span class="o">&lt;&lt;</span> <span class="n">4</span><span class="o">)</span> <span class="o">+</span> <span class="n">startIndex</span><span class="o">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">            <span class="c1">// Per-row prefix.
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>            <span class="n">appendHexDumpRowPrefix</span><span class="o">(</span><span class="n">dump</span><span class="o">,</span> <span class="n">row</span><span class="o">,</span> <span class="n">rowStartIndex</span><span class="o">);</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">            <span class="c1">// Hex dump
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>            <span class="kt">int</span> <span class="n">rowEndIndex</span> <span class="o">=</span> <span class="n">rowStartIndex</span> <span class="o">+</span> <span class="n">16</span><span class="o">;</span>
</span></span><span class="line"><span class="cl">            <span class="k">for</span> <span class="o">(</span><span class="kt">int</span> <span class="n">j</span> <span class="o">=</span> <span class="n">rowStartIndex</span><span class="o">;</span> <span class="n">j</span> <span class="o">&lt;</span> <span class="n">rowEndIndex</span><span class="o">;</span> <span class="n">j</span><span class="o">++)</span> <span class="o">{</span>
</span></span><span class="line"><span class="cl">                <span class="n">dump</span><span class="o">.</span><span class="na">append</span><span class="o">(</span><span class="n">BYTE2HEX</span><span class="o">[</span><span class="n">getUnsignedByte</span><span class="o">(</span><span class="n">buf</span><span class="o">,</span> <span class="n">j</span><span class="o">)]);</span>
</span></span><span class="line"><span class="cl">            <span class="o">}</span>
</span></span><span class="line"><span class="cl">            <span class="n">dump</span><span class="o">.</span><span class="na">append</span><span class="o">(</span><span class="s">&#34; |&#34;</span><span class="o">);</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">            <span class="c1">// ASCII dump
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>            <span class="k">for</span> <span class="o">(</span><span class="kt">int</span> <span class="n">j</span> <span class="o">=</span> <span class="n">rowStartIndex</span><span class="o">;</span> <span class="n">j</span> <span class="o">&lt;</span> <span class="n">rowEndIndex</span><span class="o">;</span> <span class="n">j</span><span class="o">++)</span> <span class="o">{</span>
</span></span><span class="line"><span class="cl">                <span class="n">dump</span><span class="o">.</span><span class="na">append</span><span class="o">(</span><span class="n">BYTE2CHAR</span><span class="o">[</span><span class="n">getUnsignedByte</span><span class="o">(</span><span class="n">buf</span><span class="o">,</span> <span class="n">j</span><span class="o">)]);</span>
</span></span><span class="line"><span class="cl">            <span class="o">}</span>
</span></span><span class="line"><span class="cl">            <span class="n">dump</span><span class="o">.</span><span class="na">append</span><span class="o">(</span><span class="sc">&#39;|&#39;</span><span class="o">);</span>
</span></span><span class="line"><span class="cl">        <span class="o">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">        <span class="c1">// Dump the last row which has less than 16 bytes.
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>        <span class="k">if</span> <span class="o">(</span><span class="n">remainder</span> <span class="o">!=</span> <span class="n">0</span><span class="o">)</span> <span class="o">{</span>
</span></span><span class="line"><span class="cl">            <span class="kt">int</span> <span class="n">rowStartIndex</span> <span class="o">=</span> <span class="o">(</span><span class="n">fullRows</span> <span class="o">&lt;&lt;</span> <span class="n">4</span><span class="o">)</span> <span class="o">+</span> <span class="n">startIndex</span><span class="o">;</span>
</span></span><span class="line"><span class="cl">            <span class="n">appendHexDumpRowPrefix</span><span class="o">(</span><span class="n">dump</span><span class="o">,</span> <span class="n">fullRows</span><span class="o">,</span> <span class="n">rowStartIndex</span><span class="o">);</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">            <span class="c1">// Hex dump
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>            <span class="kt">int</span> <span class="n">rowEndIndex</span> <span class="o">=</span> <span class="n">rowStartIndex</span> <span class="o">+</span> <span class="n">remainder</span><span class="o">;</span>
</span></span><span class="line"><span class="cl">            <span class="k">for</span> <span class="o">(</span><span class="kt">int</span> <span class="n">j</span> <span class="o">=</span> <span class="n">rowStartIndex</span><span class="o">;</span> <span class="n">j</span> <span class="o">&lt;</span> <span class="n">rowEndIndex</span><span class="o">;</span> <span class="n">j</span><span class="o">++)</span> <span class="o">{</span>
</span></span><span class="line"><span class="cl">                <span class="n">dump</span><span class="o">.</span><span class="na">append</span><span class="o">(</span><span class="n">BYTE2HEX</span><span class="o">[</span><span class="n">getUnsignedByte</span><span class="o">(</span><span class="n">buf</span><span class="o">,</span> <span class="n">j</span><span class="o">)]);</span>
</span></span><span class="line"><span class="cl">            <span class="o">}</span>
</span></span><span class="line"><span class="cl">            <span class="n">dump</span><span class="o">.</span><span class="na">append</span><span class="o">(</span><span class="n">HEXPADDING</span><span class="o">[</span><span class="n">remainder</span><span class="o">]);</span>
</span></span><span class="line"><span class="cl">            <span class="n">dump</span><span class="o">.</span><span class="na">append</span><span class="o">(</span><span class="s">&#34; |&#34;</span><span class="o">);</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">            <span class="c1">// Ascii dump
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>            <span class="k">for</span> <span class="o">(</span><span class="kt">int</span> <span class="n">j</span> <span class="o">=</span> <span class="n">rowStartIndex</span><span class="o">;</span> <span class="n">j</span> <span class="o">&lt;</span> <span class="n">rowEndIndex</span><span class="o">;</span> <span class="n">j</span><span class="o">++)</span> <span class="o">{</span>
</span></span><span class="line"><span class="cl">                <span class="n">dump</span><span class="o">.</span><span class="na">append</span><span class="o">(</span><span class="n">BYTE2CHAR</span><span class="o">[</span><span class="n">getUnsignedByte</span><span class="o">(</span><span class="n">buf</span><span class="o">,</span> <span class="n">j</span><span class="o">)]);</span>
</span></span><span class="line"><span class="cl">            <span class="o">}</span>
</span></span><span class="line"><span class="cl">            <span class="n">dump</span><span class="o">.</span><span class="na">append</span><span class="o">(</span><span class="n">BYTEPADDING</span><span class="o">[</span><span class="n">remainder</span><span class="o">]);</span>
</span></span><span class="line"><span class="cl">            <span class="n">dump</span><span class="o">.</span><span class="na">append</span><span class="o">(</span><span class="sc">&#39;|&#39;</span><span class="o">);</span>
</span></span><span class="line"><span class="cl">        <span class="o">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">        <span class="n">dump</span><span class="o">.</span><span class="na">append</span><span class="o">(</span><span class="n">NEWLINE</span> <span class="o">+</span>
</span></span><span class="line"><span class="cl">                <span class="s">&#34;+--------+-------------------------------------------------+----------------+&#34;</span><span class="o">);</span>
</span></span><span class="line"><span class="cl">    <span class="o">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="kd">private</span> <span class="kd">static</span> <span class="kt">void</span> <span class="nf">appendHexDumpRowPrefix</span><span class="o">(</span><span class="n">StringBuilder</span> <span class="n">dump</span><span class="o">,</span> <span class="kt">int</span> <span class="n">row</span><span class="o">,</span> <span class="kt">int</span> <span class="n">rowStartIndex</span><span class="o">)</span> <span class="o">{</span>
</span></span><span class="line"><span class="cl">        <span class="k">if</span> <span class="o">(</span><span class="n">row</span> <span class="o">&lt;</span> <span class="n">HEXDUMP_ROWPREFIXES</span><span class="o">.</span><span class="na">length</span><span class="o">)</span> <span class="o">{</span>
</span></span><span class="line"><span class="cl">            <span class="n">dump</span><span class="o">.</span><span class="na">append</span><span class="o">(</span><span class="n">HEXDUMP_ROWPREFIXES</span><span class="o">[</span><span class="n">row</span><span class="o">]);</span>
</span></span><span class="line"><span class="cl">        <span class="o">}</span> <span class="k">else</span> <span class="o">{</span>
</span></span><span class="line"><span class="cl">            <span class="n">dump</span><span class="o">.</span><span class="na">append</span><span class="o">(</span><span class="n">NEWLINE</span><span class="o">);</span>
</span></span><span class="line"><span class="cl">            <span class="n">dump</span><span class="o">.</span><span class="na">append</span><span class="o">(</span><span class="n">Long</span><span class="o">.</span><span class="na">toHexString</span><span class="o">(</span><span class="n">rowStartIndex</span> <span class="o">&amp;</span> <span class="n">0xFFFFFFFFL</span> <span class="o">|</span> <span class="n">0x100000000L</span><span class="o">));</span>
</span></span><span class="line"><span class="cl">            <span class="n">dump</span><span class="o">.</span><span class="na">setCharAt</span><span class="o">(</span><span class="n">dump</span><span class="o">.</span><span class="na">length</span><span class="o">()</span> <span class="o">-</span> <span class="n">9</span><span class="o">,</span> <span class="sc">&#39;|&#39;</span><span class="o">);</span>
</span></span><span class="line"><span class="cl">            <span class="n">dump</span><span class="o">.</span><span class="na">append</span><span class="o">(</span><span class="sc">&#39;|&#39;</span><span class="o">);</span>
</span></span><span class="line"><span class="cl">        <span class="o">}</span>
</span></span><span class="line"><span class="cl">    <span class="o">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="kd">public</span> <span class="kd">static</span> <span class="kt">short</span> <span class="nf">getUnsignedByte</span><span class="o">(</span><span class="n">ByteBuffer</span> <span class="n">buffer</span><span class="o">,</span> <span class="kt">int</span> <span class="n">index</span><span class="o">)</span> <span class="o">{</span>
</span></span><span class="line"><span class="cl">        <span class="k">return</span> <span class="o">(</span><span class="kt">short</span><span class="o">)</span> <span class="o">(</span><span class="n">buffer</span><span class="o">.</span><span class="na">get</span><span class="o">(</span><span class="n">index</span><span class="o">)</span> <span class="o">&amp;</span> <span class="n">0xFF</span><span class="o">);</span>
</span></span><span class="line"><span class="cl">    <span class="o">}</span>
</span></span><span class="line"><span class="cl"><span class="o">}</span>
</span></span></code></pre></td></tr></table>
</div>
</div><h3 id="23-bytebuffer-常见方法">2.3 ByteBuffer 常见方法</h3>
<h4 id="分配空间">分配空间</h4>
<p>可以使用 allocate 方法为 ByteBuffer 分配空间，其它 buffer 类也有该方法</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-java" data-lang="java"><span class="line"><span class="cl"><span class="n">Bytebuffer</span> <span class="n">buf</span> <span class="o">=</span> <span class="n">ByteBuffer</span><span class="o">.</span><span class="na">allocate</span><span class="o">(</span><span class="n">16</span><span class="o">);</span>
</span></span></code></pre></td></tr></table>
</div>
</div><h4 id="向-buffer-写入数据">向 buffer 写入数据</h4>
<p>有两种办法</p>
<ul>
<li>调用 channel 的 read 方法</li>
<li>调用 buffer 自己的 put 方法</li>
</ul>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-java" data-lang="java"><span class="line"><span class="cl"><span class="kt">int</span> <span class="n">readBytes</span> <span class="o">=</span> <span class="n">channel</span><span class="o">.</span><span class="na">read</span><span class="o">(</span><span class="n">buf</span><span class="o">);</span>
</span></span></code></pre></td></tr></table>
</div>
</div><p>和</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-java" data-lang="java"><span class="line"><span class="cl"><span class="n">buf</span><span class="o">.</span><span class="na">put</span><span class="o">((</span><span class="kt">byte</span><span class="o">)</span><span class="n">127</span><span class="o">);</span>
</span></span></code></pre></td></tr></table>
</div>
</div><h4 id="从-buffer-读取数据">从 buffer 读取数据</h4>
<p>同样有两种办法</p>
<ul>
<li>调用 channel 的 write 方法</li>
<li>调用 buffer 自己的 get 方法</li>
</ul>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-java" data-lang="java"><span class="line"><span class="cl"><span class="kt">int</span> <span class="n">writeBytes</span> <span class="o">=</span> <span class="n">channel</span><span class="o">.</span><span class="na">write</span><span class="o">(</span><span class="n">buf</span><span class="o">);</span>
</span></span></code></pre></td></tr></table>
</div>
</div><p>和</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-java" data-lang="java"><span class="line"><span class="cl"><span class="kt">byte</span> <span class="n">b</span> <span class="o">=</span> <span class="n">buf</span><span class="o">.</span><span class="na">get</span><span class="o">();</span>
</span></span></code></pre></td></tr></table>
</div>
</div><p>get 方法会让 position 读指针向后走，如果想重复读取数据</p>
<ul>
<li>可以调用 rewind 方法将 position 重新置为 0</li>
<li>或者调用 get(int i) 方法获取索引 i 的内容，它不会移动读指针</li>
</ul>
<h4 id="mark-和-reset">mark 和 reset</h4>
<p>mark 是在读取时，做一个标记，即使 position 改变，只要调用 reset 就能回到 mark 的位置</p>
<blockquote>
<p><strong>注意</strong></p>
<p>rewind 和 flip 都会清除 mark 位置</p>
</blockquote>
<h4 id="字符串与-bytebuffer-互转">字符串与 ByteBuffer 互转</h4>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span><span class="lnt">2
</span><span class="lnt">3
</span><span class="lnt">4
</span><span class="lnt">5
</span><span class="lnt">6
</span><span class="lnt">7
</span><span class="lnt">8
</span><span class="lnt">9
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-java" data-lang="java"><span class="line"><span class="cl"><span class="n">ByteBuffer</span> <span class="n">buffer1</span> <span class="o">=</span> <span class="n">StandardCharsets</span><span class="o">.</span><span class="na">UTF_8</span><span class="o">.</span><span class="na">encode</span><span class="o">(</span><span class="s">&#34;你好&#34;</span><span class="o">);</span>
</span></span><span class="line"><span class="cl"><span class="n">ByteBuffer</span> <span class="n">buffer2</span> <span class="o">=</span> <span class="n">Charset</span><span class="o">.</span><span class="na">forName</span><span class="o">(</span><span class="s">&#34;utf-8&#34;</span><span class="o">).</span><span class="na">encode</span><span class="o">(</span><span class="s">&#34;你好&#34;</span><span class="o">);</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="n">debug</span><span class="o">(</span><span class="n">buffer1</span><span class="o">);</span>
</span></span><span class="line"><span class="cl"><span class="n">debug</span><span class="o">(</span><span class="n">buffer2</span><span class="o">);</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="n">CharBuffer</span> <span class="n">buffer3</span> <span class="o">=</span> <span class="n">StandardCharsets</span><span class="o">.</span><span class="na">UTF_8</span><span class="o">.</span><span class="na">decode</span><span class="o">(</span><span class="n">buffer1</span><span class="o">);</span>
</span></span><span class="line"><span class="cl"><span class="n">System</span><span class="o">.</span><span class="na">out</span><span class="o">.</span><span class="na">println</span><span class="o">(</span><span class="n">buffer3</span><span class="o">.</span><span class="na">getClass</span><span class="o">());</span>
</span></span><span class="line"><span class="cl"><span class="n">System</span><span class="o">.</span><span class="na">out</span><span class="o">.</span><span class="na">println</span><span class="o">(</span><span class="n">buffer3</span><span class="o">.</span><span class="na">toString</span><span class="o">());</span>
</span></span></code></pre></td></tr></table>
</div>
</div><p>输出</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt"> 1
</span><span class="lnt"> 2
</span><span class="lnt"> 3
</span><span class="lnt"> 4
</span><span class="lnt"> 5
</span><span class="lnt"> 6
</span><span class="lnt"> 7
</span><span class="lnt"> 8
</span><span class="lnt"> 9
</span><span class="lnt">10
</span><span class="lnt">11
</span><span class="lnt">12
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-fallback" data-lang="fallback"><span class="line"><span class="cl">         +-------------------------------------------------+
</span></span><span class="line"><span class="cl">         |  0  1  2  3  4  5  6  7  8  9  a  b  c  d  e  f |
</span></span><span class="line"><span class="cl">+--------+-------------------------------------------------+----------------+
</span></span><span class="line"><span class="cl">|00000000| e4 bd a0 e5 a5 bd                               |......          |
</span></span><span class="line"><span class="cl">+--------+-------------------------------------------------+----------------+
</span></span><span class="line"><span class="cl">         +-------------------------------------------------+
</span></span><span class="line"><span class="cl">         |  0  1  2  3  4  5  6  7  8  9  a  b  c  d  e  f |
</span></span><span class="line"><span class="cl">+--------+-------------------------------------------------+----------------+
</span></span><span class="line"><span class="cl">|00000000| e4 bd a0 e5 a5 bd                               |......          |
</span></span><span class="line"><span class="cl">+--------+-------------------------------------------------+----------------+
</span></span><span class="line"><span class="cl">class java.nio.HeapCharBuffer
</span></span><span class="line"><span class="cl">你好
</span></span></code></pre></td></tr></table>
</div>
</div><h4 id="-buffer-的线程安全">⚠️ Buffer 的线程安全</h4>
<blockquote>
<p>Buffer 是<strong>非线程安全的</strong></p>
</blockquote>
<h3 id="24-scattering-reads">2.4 Scattering Reads</h3>
<p>分散读取，有一个文本文件 3parts.txt</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-fallback" data-lang="fallback"><span class="line"><span class="cl">onetwothree
</span></span></code></pre></td></tr></table>
</div>
</div><p>使用如下方式读取，可以将数据填充至多个 buffer</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt"> 1
</span><span class="lnt"> 2
</span><span class="lnt"> 3
</span><span class="lnt"> 4
</span><span class="lnt"> 5
</span><span class="lnt"> 6
</span><span class="lnt"> 7
</span><span class="lnt"> 8
</span><span class="lnt"> 9
</span><span class="lnt">10
</span><span class="lnt">11
</span><span class="lnt">12
</span><span class="lnt">13
</span><span class="lnt">14
</span><span class="lnt">15
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-java" data-lang="java"><span class="line"><span class="cl"><span class="k">try</span> <span class="o">(</span><span class="n">RandomAccessFile</span> <span class="n">file</span> <span class="o">=</span> <span class="k">new</span> <span class="n">RandomAccessFile</span><span class="o">(</span><span class="s">&#34;helloword/3parts.txt&#34;</span><span class="o">,</span> <span class="s">&#34;rw&#34;</span><span class="o">))</span> <span class="o">{</span>
</span></span><span class="line"><span class="cl">    <span class="n">FileChannel</span> <span class="n">channel</span> <span class="o">=</span> <span class="n">file</span><span class="o">.</span><span class="na">getChannel</span><span class="o">();</span>
</span></span><span class="line"><span class="cl">    <span class="n">ByteBuffer</span> <span class="n">a</span> <span class="o">=</span> <span class="n">ByteBuffer</span><span class="o">.</span><span class="na">allocate</span><span class="o">(</span><span class="n">3</span><span class="o">);</span>
</span></span><span class="line"><span class="cl">    <span class="n">ByteBuffer</span> <span class="n">b</span> <span class="o">=</span> <span class="n">ByteBuffer</span><span class="o">.</span><span class="na">allocate</span><span class="o">(</span><span class="n">3</span><span class="o">);</span>
</span></span><span class="line"><span class="cl">    <span class="n">ByteBuffer</span> <span class="n">c</span> <span class="o">=</span> <span class="n">ByteBuffer</span><span class="o">.</span><span class="na">allocate</span><span class="o">(</span><span class="n">5</span><span class="o">);</span>
</span></span><span class="line"><span class="cl">    <span class="n">channel</span><span class="o">.</span><span class="na">read</span><span class="o">(</span><span class="k">new</span> <span class="n">ByteBuffer</span><span class="o">[]{</span><span class="n">a</span><span class="o">,</span> <span class="n">b</span><span class="o">,</span> <span class="n">c</span><span class="o">});</span>
</span></span><span class="line"><span class="cl">    <span class="n">a</span><span class="o">.</span><span class="na">flip</span><span class="o">();</span>
</span></span><span class="line"><span class="cl">    <span class="n">b</span><span class="o">.</span><span class="na">flip</span><span class="o">();</span>
</span></span><span class="line"><span class="cl">    <span class="n">c</span><span class="o">.</span><span class="na">flip</span><span class="o">();</span>
</span></span><span class="line"><span class="cl">    <span class="n">debug</span><span class="o">(</span><span class="n">a</span><span class="o">);</span>
</span></span><span class="line"><span class="cl">    <span class="n">debug</span><span class="o">(</span><span class="n">b</span><span class="o">);</span>
</span></span><span class="line"><span class="cl">    <span class="n">debug</span><span class="o">(</span><span class="n">c</span><span class="o">);</span>
</span></span><span class="line"><span class="cl"><span class="o">}</span> <span class="k">catch</span> <span class="o">(</span><span class="n">IOException</span> <span class="n">e</span><span class="o">)</span> <span class="o">{</span>
</span></span><span class="line"><span class="cl">    <span class="n">e</span><span class="o">.</span><span class="na">printStackTrace</span><span class="o">();</span>
</span></span><span class="line"><span class="cl"><span class="o">}</span>
</span></span></code></pre></td></tr></table>
</div>
</div><p>结果</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt"> 1
</span><span class="lnt"> 2
</span><span class="lnt"> 3
</span><span class="lnt"> 4
</span><span class="lnt"> 5
</span><span class="lnt"> 6
</span><span class="lnt"> 7
</span><span class="lnt"> 8
</span><span class="lnt"> 9
</span><span class="lnt">10
</span><span class="lnt">11
</span><span class="lnt">12
</span><span class="lnt">13
</span><span class="lnt">14
</span><span class="lnt">15
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-fallback" data-lang="fallback"><span class="line"><span class="cl">         +-------------------------------------------------+
</span></span><span class="line"><span class="cl">         |  0  1  2  3  4  5  6  7  8  9  a  b  c  d  e  f |
</span></span><span class="line"><span class="cl">+--------+-------------------------------------------------+----------------+
</span></span><span class="line"><span class="cl">|00000000| 6f 6e 65                                        |one             |
</span></span><span class="line"><span class="cl">+--------+-------------------------------------------------+----------------+
</span></span><span class="line"><span class="cl">         +-------------------------------------------------+
</span></span><span class="line"><span class="cl">         |  0  1  2  3  4  5  6  7  8  9  a  b  c  d  e  f |
</span></span><span class="line"><span class="cl">+--------+-------------------------------------------------+----------------+
</span></span><span class="line"><span class="cl">|00000000| 74 77 6f                                        |two             |
</span></span><span class="line"><span class="cl">+--------+-------------------------------------------------+----------------+
</span></span><span class="line"><span class="cl">         +-------------------------------------------------+
</span></span><span class="line"><span class="cl">         |  0  1  2  3  4  5  6  7  8  9  a  b  c  d  e  f |
</span></span><span class="line"><span class="cl">+--------+-------------------------------------------------+----------------+
</span></span><span class="line"><span class="cl">|00000000| 74 68 72 65 65                                  |three           |
</span></span><span class="line"><span class="cl">+--------+-------------------------------------------------+----------------+
</span></span></code></pre></td></tr></table>
</div>
</div><h3 id="25-gathering-writes">2.5 Gathering Writes</h3>
<p>使用如下方式写入，可以将多个 buffer 的数据填充至 channel</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt"> 1
</span><span class="lnt"> 2
</span><span class="lnt"> 3
</span><span class="lnt"> 4
</span><span class="lnt"> 5
</span><span class="lnt"> 6
</span><span class="lnt"> 7
</span><span class="lnt"> 8
</span><span class="lnt"> 9
</span><span class="lnt">10
</span><span class="lnt">11
</span><span class="lnt">12
</span><span class="lnt">13
</span><span class="lnt">14
</span><span class="lnt">15
</span><span class="lnt">16
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-java" data-lang="java"><span class="line"><span class="cl"><span class="k">try</span> <span class="o">(</span><span class="n">RandomAccessFile</span> <span class="n">file</span> <span class="o">=</span> <span class="k">new</span> <span class="n">RandomAccessFile</span><span class="o">(</span><span class="s">&#34;helloword/3parts.txt&#34;</span><span class="o">,</span> <span class="s">&#34;rw&#34;</span><span class="o">))</span> <span class="o">{</span>
</span></span><span class="line"><span class="cl">    <span class="n">FileChannel</span> <span class="n">channel</span> <span class="o">=</span> <span class="n">file</span><span class="o">.</span><span class="na">getChannel</span><span class="o">();</span>
</span></span><span class="line"><span class="cl">    <span class="n">ByteBuffer</span> <span class="n">d</span> <span class="o">=</span> <span class="n">ByteBuffer</span><span class="o">.</span><span class="na">allocate</span><span class="o">(</span><span class="n">4</span><span class="o">);</span>
</span></span><span class="line"><span class="cl">    <span class="n">ByteBuffer</span> <span class="n">e</span> <span class="o">=</span> <span class="n">ByteBuffer</span><span class="o">.</span><span class="na">allocate</span><span class="o">(</span><span class="n">4</span><span class="o">);</span>
</span></span><span class="line"><span class="cl">    <span class="n">channel</span><span class="o">.</span><span class="na">position</span><span class="o">(</span><span class="n">11</span><span class="o">);</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="n">d</span><span class="o">.</span><span class="na">put</span><span class="o">(</span><span class="k">new</span> <span class="kt">byte</span><span class="o">[]{</span><span class="sc">&#39;f&#39;</span><span class="o">,</span> <span class="sc">&#39;o&#39;</span><span class="o">,</span> <span class="sc">&#39;u&#39;</span><span class="o">,</span> <span class="sc">&#39;r&#39;</span><span class="o">});</span>
</span></span><span class="line"><span class="cl">    <span class="n">e</span><span class="o">.</span><span class="na">put</span><span class="o">(</span><span class="k">new</span> <span class="kt">byte</span><span class="o">[]{</span><span class="sc">&#39;f&#39;</span><span class="o">,</span> <span class="sc">&#39;i&#39;</span><span class="o">,</span> <span class="sc">&#39;v&#39;</span><span class="o">,</span> <span class="sc">&#39;e&#39;</span><span class="o">});</span>
</span></span><span class="line"><span class="cl">    <span class="n">d</span><span class="o">.</span><span class="na">flip</span><span class="o">();</span>
</span></span><span class="line"><span class="cl">    <span class="n">e</span><span class="o">.</span><span class="na">flip</span><span class="o">();</span>
</span></span><span class="line"><span class="cl">    <span class="n">debug</span><span class="o">(</span><span class="n">d</span><span class="o">);</span>
</span></span><span class="line"><span class="cl">    <span class="n">debug</span><span class="o">(</span><span class="n">e</span><span class="o">);</span>
</span></span><span class="line"><span class="cl">    <span class="n">channel</span><span class="o">.</span><span class="na">write</span><span class="o">(</span><span class="k">new</span> <span class="n">ByteBuffer</span><span class="o">[]{</span><span class="n">d</span><span class="o">,</span> <span class="n">e</span><span class="o">});</span>
</span></span><span class="line"><span class="cl"><span class="o">}</span> <span class="k">catch</span> <span class="o">(</span><span class="n">IOException</span> <span class="n">e</span><span class="o">)</span> <span class="o">{</span>
</span></span><span class="line"><span class="cl">    <span class="n">e</span><span class="o">.</span><span class="na">printStackTrace</span><span class="o">();</span>
</span></span><span class="line"><span class="cl"><span class="o">}</span>
</span></span></code></pre></td></tr></table>
</div>
</div><p>输出</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt"> 1
</span><span class="lnt"> 2
</span><span class="lnt"> 3
</span><span class="lnt"> 4
</span><span class="lnt"> 5
</span><span class="lnt"> 6
</span><span class="lnt"> 7
</span><span class="lnt"> 8
</span><span class="lnt"> 9
</span><span class="lnt">10
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-fallback" data-lang="fallback"><span class="line"><span class="cl">         +-------------------------------------------------+
</span></span><span class="line"><span class="cl">         |  0  1  2  3  4  5  6  7  8  9  a  b  c  d  e  f |
</span></span><span class="line"><span class="cl">+--------+-------------------------------------------------+----------------+
</span></span><span class="line"><span class="cl">|00000000| 66 6f 75 72                                     |four            |
</span></span><span class="line"><span class="cl">+--------+-------------------------------------------------+----------------+
</span></span><span class="line"><span class="cl">         +-------------------------------------------------+
</span></span><span class="line"><span class="cl">         |  0  1  2  3  4  5  6  7  8  9  a  b  c  d  e  f |
</span></span><span class="line"><span class="cl">+--------+-------------------------------------------------+----------------+
</span></span><span class="line"><span class="cl">|00000000| 66 69 76 65                                     |five            |
</span></span><span class="line"><span class="cl">+--------+-------------------------------------------------+----------------+
</span></span></code></pre></td></tr></table>
</div>
</div><p>文件内容</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-fallback" data-lang="fallback"><span class="line"><span class="cl">onetwothreefourfive
</span></span></code></pre></td></tr></table>
</div>
</div><h3 id="26-练习">2.6 练习</h3>
<p>网络上有多条数据发送给服务端，数据之间使用 \n 进行分隔
但由于某种原因这些数据在接收时，被进行了重新组合，例如原始数据有3条为</p>
<ul>
<li>Hello,world\n</li>
<li>I&rsquo;m zhangsan\n</li>
<li>How are you?\n</li>
</ul>
<p>变成了下面的两个 byteBuffer (黏包，半包)</p>
<ul>
<li>Hello,world\nI&rsquo;m zhangsan\nHo</li>
<li>w are you?\n</li>
</ul>
<p>现在要求你编写程序，将错乱的数据恢复成原始的按 \n 分隔的数据</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt"> 1
</span><span class="lnt"> 2
</span><span class="lnt"> 3
</span><span class="lnt"> 4
</span><span class="lnt"> 5
</span><span class="lnt"> 6
</span><span class="lnt"> 7
</span><span class="lnt"> 8
</span><span class="lnt"> 9
</span><span class="lnt">10
</span><span class="lnt">11
</span><span class="lnt">12
</span><span class="lnt">13
</span><span class="lnt">14
</span><span class="lnt">15
</span><span class="lnt">16
</span><span class="lnt">17
</span><span class="lnt">18
</span><span class="lnt">19
</span><span class="lnt">20
</span><span class="lnt">21
</span><span class="lnt">22
</span><span class="lnt">23
</span><span class="lnt">24
</span><span class="lnt">25
</span><span class="lnt">26
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-java" data-lang="java"><span class="line"><span class="cl"><span class="kd">public</span> <span class="kd">static</span> <span class="kt">void</span> <span class="nf">main</span><span class="o">(</span><span class="n">String</span><span class="o">[]</span> <span class="n">args</span><span class="o">)</span> <span class="o">{</span>
</span></span><span class="line"><span class="cl">    <span class="n">ByteBuffer</span> <span class="n">source</span> <span class="o">=</span> <span class="n">ByteBuffer</span><span class="o">.</span><span class="na">allocate</span><span class="o">(</span><span class="n">32</span><span class="o">);</span>
</span></span><span class="line"><span class="cl">    <span class="c1">//                     11            24
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="n">source</span><span class="o">.</span><span class="na">put</span><span class="o">(</span><span class="s">&#34;Hello,world\nI&#39;m zhangsan\nHo&#34;</span><span class="o">.</span><span class="na">getBytes</span><span class="o">());</span>
</span></span><span class="line"><span class="cl">    <span class="n">split</span><span class="o">(</span><span class="n">source</span><span class="o">);</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="n">source</span><span class="o">.</span><span class="na">put</span><span class="o">(</span><span class="s">&#34;w are you?\nhaha!\n&#34;</span><span class="o">.</span><span class="na">getBytes</span><span class="o">());</span>
</span></span><span class="line"><span class="cl">    <span class="n">split</span><span class="o">(</span><span class="n">source</span><span class="o">);</span>
</span></span><span class="line"><span class="cl"><span class="o">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kd">private</span> <span class="kd">static</span> <span class="kt">void</span> <span class="nf">split</span><span class="o">(</span><span class="n">ByteBuffer</span> <span class="n">source</span><span class="o">)</span> <span class="o">{</span>
</span></span><span class="line"><span class="cl">    <span class="n">source</span><span class="o">.</span><span class="na">flip</span><span class="o">();</span>
</span></span><span class="line"><span class="cl">    <span class="kt">int</span> <span class="n">oldLimit</span> <span class="o">=</span> <span class="n">source</span><span class="o">.</span><span class="na">limit</span><span class="o">();</span>
</span></span><span class="line"><span class="cl">    <span class="k">for</span> <span class="o">(</span><span class="kt">int</span> <span class="n">i</span> <span class="o">=</span> <span class="n">0</span><span class="o">;</span> <span class="n">i</span> <span class="o">&lt;</span> <span class="n">oldLimit</span><span class="o">;</span> <span class="n">i</span><span class="o">++)</span> <span class="o">{</span>
</span></span><span class="line"><span class="cl">        <span class="k">if</span> <span class="o">(</span><span class="n">source</span><span class="o">.</span><span class="na">get</span><span class="o">(</span><span class="n">i</span><span class="o">)</span> <span class="o">==</span> <span class="sc">&#39;\n&#39;</span><span class="o">)</span> <span class="o">{</span>
</span></span><span class="line"><span class="cl">            <span class="n">System</span><span class="o">.</span><span class="na">out</span><span class="o">.</span><span class="na">println</span><span class="o">(</span><span class="n">i</span><span class="o">);</span>
</span></span><span class="line"><span class="cl">            <span class="n">ByteBuffer</span> <span class="n">target</span> <span class="o">=</span> <span class="n">ByteBuffer</span><span class="o">.</span><span class="na">allocate</span><span class="o">(</span><span class="n">i</span> <span class="o">+</span> <span class="n">1</span> <span class="o">-</span> <span class="n">source</span><span class="o">.</span><span class="na">position</span><span class="o">());</span>
</span></span><span class="line"><span class="cl">            <span class="c1">// 0 ~ limit
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>            <span class="n">source</span><span class="o">.</span><span class="na">limit</span><span class="o">(</span><span class="n">i</span> <span class="o">+</span> <span class="n">1</span><span class="o">);</span>
</span></span><span class="line"><span class="cl">            <span class="n">target</span><span class="o">.</span><span class="na">put</span><span class="o">(</span><span class="n">source</span><span class="o">);</span> <span class="c1">// 从source 读，向 target 写
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>            <span class="n">debugAll</span><span class="o">(</span><span class="n">target</span><span class="o">);</span>
</span></span><span class="line"><span class="cl">            <span class="n">source</span><span class="o">.</span><span class="na">limit</span><span class="o">(</span><span class="n">oldLimit</span><span class="o">);</span>
</span></span><span class="line"><span class="cl">        <span class="o">}</span>
</span></span><span class="line"><span class="cl">    <span class="o">}</span>
</span></span><span class="line"><span class="cl">    <span class="n">source</span><span class="o">.</span><span class="na">compact</span><span class="o">();</span>
</span></span><span class="line"><span class="cl"><span class="o">}</span>
</span></span></code></pre></td></tr></table>
</div>
</div><h2 id="3-文件编程">3. 文件编程</h2>
<h3 id="31-filechannel">3.1 FileChannel</h3>
<h4 id="-filechannel-工作模式">⚠️ FileChannel 工作模式</h4>
<blockquote>
<p>FileChannel 只能工作在阻塞模式下</p>
</blockquote>
<h4 id="获取">获取</h4>
<p>不能直接打开 FileChannel，必须通过 FileInputStream、FileOutputStream 或者 RandomAccessFile 来获取 FileChannel，它们都有 getChannel 方法</p>
<ul>
<li>通过 FileInputStream 获取的 channel 只能读</li>
<li>通过 FileOutputStream 获取的 channel 只能写</li>
<li>通过 RandomAccessFile 是否能读写根据构造 RandomAccessFile 时的读写模式决定</li>
</ul>
<h4 id="读取">读取</h4>
<p>会从 channel 读取数据填充 ByteBuffer，返回值表示读到了多少字节，-1 表示到达了文件的末尾</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-java" data-lang="java"><span class="line"><span class="cl"><span class="kt">int</span> <span class="n">readBytes</span> <span class="o">=</span> <span class="n">channel</span><span class="o">.</span><span class="na">read</span><span class="o">(</span><span class="n">buffer</span><span class="o">);</span>
</span></span></code></pre></td></tr></table>
</div>
</div><h4 id="写入">写入</h4>
<p>写入的正确姿势如下， SocketChannel</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span><span class="lnt">2
</span><span class="lnt">3
</span><span class="lnt">4
</span><span class="lnt">5
</span><span class="lnt">6
</span><span class="lnt">7
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-java" data-lang="java"><span class="line"><span class="cl"><span class="n">ByteBuffer</span> <span class="n">buffer</span> <span class="o">=</span> <span class="o">...;</span>
</span></span><span class="line"><span class="cl"><span class="n">buffer</span><span class="o">.</span><span class="na">put</span><span class="o">(...);</span> <span class="c1">// 存入数据
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="n">buffer</span><span class="o">.</span><span class="na">flip</span><span class="o">();</span>   <span class="c1">// 切换读模式
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>
</span></span><span class="line"><span class="cl"><span class="k">while</span><span class="o">(</span><span class="n">buffer</span><span class="o">.</span><span class="na">hasRemaining</span><span class="o">())</span> <span class="o">{</span>
</span></span><span class="line"><span class="cl">    <span class="n">channel</span><span class="o">.</span><span class="na">write</span><span class="o">(</span><span class="n">buffer</span><span class="o">);</span>
</span></span><span class="line"><span class="cl"><span class="o">}</span>
</span></span></code></pre></td></tr></table>
</div>
</div><p>在 while 中调用 channel.write 是因为 write 方法并不能保证一次将 buffer 中的内容全部写入 channel</p>
<h4 id="关闭">关闭</h4>
<p>channel 必须关闭，不过调用了 FileInputStream、FileOutputStream 或者 RandomAccessFile 的 close 方法会间接地调用 channel 的 close 方法</p>
<h4 id="位置">位置</h4>
<p>获取当前位置</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-java" data-lang="java"><span class="line"><span class="cl"><span class="kt">long</span> <span class="n">pos</span> <span class="o">=</span> <span class="n">channel</span><span class="o">.</span><span class="na">position</span><span class="o">();</span>
</span></span></code></pre></td></tr></table>
</div>
</div><p>设置当前位置</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span><span class="lnt">2
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-java" data-lang="java"><span class="line"><span class="cl"><span class="kt">long</span> <span class="n">newPos</span> <span class="o">=</span> <span class="o">...;</span>
</span></span><span class="line"><span class="cl"><span class="n">channel</span><span class="o">.</span><span class="na">position</span><span class="o">(</span><span class="n">newPos</span><span class="o">);</span>
</span></span></code></pre></td></tr></table>
</div>
</div><p>设置当前位置时，如果设置为文件的末尾</p>
<ul>
<li>这时读取会返回 -1</li>
<li>这时写入，会追加内容，但要注意如果 position 超过了文件末尾，再写入时在新内容和原末尾之间会有空洞（00）</li>
</ul>
<h4 id="大小">大小</h4>
<p>使用 size 方法获取文件的大小</p>
<h4 id="强制写入">强制写入</h4>
<p>操作系统出于性能的考虑，会将数据缓存，不是立刻写入磁盘。可以调用 force(true)  方法将文件内容和元数据（文件的权限等信息）立刻写入磁盘</p>
<h3 id="32-两个-channel-传输数据">3.2 两个 Channel 传输数据</h3>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt"> 1
</span><span class="lnt"> 2
</span><span class="lnt"> 3
</span><span class="lnt"> 4
</span><span class="lnt"> 5
</span><span class="lnt"> 6
</span><span class="lnt"> 7
</span><span class="lnt"> 8
</span><span class="lnt"> 9
</span><span class="lnt">10
</span><span class="lnt">11
</span><span class="lnt">12
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-java" data-lang="java"><span class="line"><span class="cl"><span class="n">String</span> <span class="n">FROM</span> <span class="o">=</span> <span class="s">&#34;helloword/data.txt&#34;</span><span class="o">;</span>
</span></span><span class="line"><span class="cl"><span class="n">String</span> <span class="n">TO</span> <span class="o">=</span> <span class="s">&#34;helloword/to.txt&#34;</span><span class="o">;</span>
</span></span><span class="line"><span class="cl"><span class="kt">long</span> <span class="n">start</span> <span class="o">=</span> <span class="n">System</span><span class="o">.</span><span class="na">nanoTime</span><span class="o">();</span>
</span></span><span class="line"><span class="cl"><span class="k">try</span> <span class="o">(</span><span class="n">FileChannel</span> <span class="n">from</span> <span class="o">=</span> <span class="k">new</span> <span class="n">FileInputStream</span><span class="o">(</span><span class="n">FROM</span><span class="o">).</span><span class="na">getChannel</span><span class="o">();</span>
</span></span><span class="line"><span class="cl">     <span class="n">FileChannel</span> <span class="n">to</span> <span class="o">=</span> <span class="k">new</span> <span class="n">FileOutputStream</span><span class="o">(</span><span class="n">TO</span><span class="o">).</span><span class="na">getChannel</span><span class="o">();</span>
</span></span><span class="line"><span class="cl">    <span class="o">)</span> <span class="o">{</span>
</span></span><span class="line"><span class="cl">    <span class="n">from</span><span class="o">.</span><span class="na">transferTo</span><span class="o">(</span><span class="n">0</span><span class="o">,</span> <span class="n">from</span><span class="o">.</span><span class="na">size</span><span class="o">(),</span> <span class="n">to</span><span class="o">);</span>
</span></span><span class="line"><span class="cl"><span class="o">}</span> <span class="k">catch</span> <span class="o">(</span><span class="n">IOException</span> <span class="n">e</span><span class="o">)</span> <span class="o">{</span>
</span></span><span class="line"><span class="cl">    <span class="n">e</span><span class="o">.</span><span class="na">printStackTrace</span><span class="o">();</span>
</span></span><span class="line"><span class="cl"><span class="o">}</span>
</span></span><span class="line"><span class="cl"><span class="kt">long</span> <span class="n">end</span> <span class="o">=</span> <span class="n">System</span><span class="o">.</span><span class="na">nanoTime</span><span class="o">();</span>
</span></span><span class="line"><span class="cl"><span class="n">System</span><span class="o">.</span><span class="na">out</span><span class="o">.</span><span class="na">println</span><span class="o">(</span><span class="s">&#34;transferTo 用时：&#34;</span> <span class="o">+</span> <span class="o">(</span><span class="n">end</span> <span class="o">-</span> <span class="n">start</span><span class="o">)</span> <span class="o">/</span> <span class="n">1000_000</span><span class="o">.</span><span class="na">0</span><span class="o">);</span>
</span></span></code></pre></td></tr></table>
</div>
</div><p>输出</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-fallback" data-lang="fallback"><span class="line"><span class="cl">transferTo 用时：8.2011
</span></span></code></pre></td></tr></table>
</div>
</div><p>超过 2g 大小的文件传输</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt"> 1
</span><span class="lnt"> 2
</span><span class="lnt"> 3
</span><span class="lnt"> 4
</span><span class="lnt"> 5
</span><span class="lnt"> 6
</span><span class="lnt"> 7
</span><span class="lnt"> 8
</span><span class="lnt"> 9
</span><span class="lnt">10
</span><span class="lnt">11
</span><span class="lnt">12
</span><span class="lnt">13
</span><span class="lnt">14
</span><span class="lnt">15
</span><span class="lnt">16
</span><span class="lnt">17
</span><span class="lnt">18
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-java" data-lang="java"><span class="line"><span class="cl"><span class="kd">public</span> <span class="kd">class</span> <span class="nc">TestFileChannelTransferTo</span> <span class="o">{</span>
</span></span><span class="line"><span class="cl">    <span class="kd">public</span> <span class="kd">static</span> <span class="kt">void</span> <span class="nf">main</span><span class="o">(</span><span class="n">String</span><span class="o">[]</span> <span class="n">args</span><span class="o">)</span> <span class="o">{</span>
</span></span><span class="line"><span class="cl">        <span class="k">try</span> <span class="o">(</span>
</span></span><span class="line"><span class="cl">                <span class="n">FileChannel</span> <span class="n">from</span> <span class="o">=</span> <span class="k">new</span> <span class="n">FileInputStream</span><span class="o">(</span><span class="s">&#34;data.txt&#34;</span><span class="o">).</span><span class="na">getChannel</span><span class="o">();</span>
</span></span><span class="line"><span class="cl">                <span class="n">FileChannel</span> <span class="n">to</span> <span class="o">=</span> <span class="k">new</span> <span class="n">FileOutputStream</span><span class="o">(</span><span class="s">&#34;to.txt&#34;</span><span class="o">).</span><span class="na">getChannel</span><span class="o">();</span>
</span></span><span class="line"><span class="cl">        <span class="o">)</span> <span class="o">{</span>
</span></span><span class="line"><span class="cl">            <span class="c1">// 效率高，底层会利用操作系统的零拷贝进行优化
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>            <span class="kt">long</span> <span class="n">size</span> <span class="o">=</span> <span class="n">from</span><span class="o">.</span><span class="na">size</span><span class="o">();</span>
</span></span><span class="line"><span class="cl">            <span class="c1">// left 变量代表还剩余多少字节
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>            <span class="k">for</span> <span class="o">(</span><span class="kt">long</span> <span class="n">left</span> <span class="o">=</span> <span class="n">size</span><span class="o">;</span> <span class="n">left</span> <span class="o">&gt;</span> <span class="n">0</span><span class="o">;</span> <span class="o">)</span> <span class="o">{</span>
</span></span><span class="line"><span class="cl">                <span class="n">System</span><span class="o">.</span><span class="na">out</span><span class="o">.</span><span class="na">println</span><span class="o">(</span><span class="s">&#34;position:&#34;</span> <span class="o">+</span> <span class="o">(</span><span class="n">size</span> <span class="o">-</span> <span class="n">left</span><span class="o">)</span> <span class="o">+</span> <span class="s">&#34; left:&#34;</span> <span class="o">+</span> <span class="n">left</span><span class="o">);</span>
</span></span><span class="line"><span class="cl">                <span class="n">left</span> <span class="o">-=</span> <span class="n">from</span><span class="o">.</span><span class="na">transferTo</span><span class="o">((</span><span class="n">size</span> <span class="o">-</span> <span class="n">left</span><span class="o">),</span> <span class="n">left</span><span class="o">,</span> <span class="n">to</span><span class="o">);</span>
</span></span><span class="line"><span class="cl">            <span class="o">}</span>
</span></span><span class="line"><span class="cl">        <span class="o">}</span> <span class="k">catch</span> <span class="o">(</span><span class="n">IOException</span> <span class="n">e</span><span class="o">)</span> <span class="o">{</span>
</span></span><span class="line"><span class="cl">            <span class="n">e</span><span class="o">.</span><span class="na">printStackTrace</span><span class="o">();</span>
</span></span><span class="line"><span class="cl">        <span class="o">}</span>
</span></span><span class="line"><span class="cl">    <span class="o">}</span>
</span></span><span class="line"><span class="cl"><span class="o">}</span>
</span></span></code></pre></td></tr></table>
</div>
</div><p>实际传输一个超大文件</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span><span class="lnt">2
</span><span class="lnt">3
</span><span class="lnt">4
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-fallback" data-lang="fallback"><span class="line"><span class="cl">position:0 left:7769948160
</span></span><span class="line"><span class="cl">position:2147483647 left:5622464513
</span></span><span class="line"><span class="cl">position:4294967294 left:3474980866
</span></span><span class="line"><span class="cl">position:6442450941 left:1327497219
</span></span></code></pre></td></tr></table>
</div>
</div><h3 id="33-path">3.3 Path</h3>
<p>jdk7 引入了 Path 和 Paths 类</p>
<ul>
<li>Path 用来表示文件路径</li>
<li>Paths 是工具类，用来获取 Path 实例</li>
</ul>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span><span class="lnt">2
</span><span class="lnt">3
</span><span class="lnt">4
</span><span class="lnt">5
</span><span class="lnt">6
</span><span class="lnt">7
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-java" data-lang="java"><span class="line"><span class="cl"><span class="n">Path</span> <span class="n">source</span> <span class="o">=</span> <span class="n">Paths</span><span class="o">.</span><span class="na">get</span><span class="o">(</span><span class="s">&#34;1.txt&#34;</span><span class="o">);</span> <span class="c1">// 相对路径 使用 user.dir 环境变量来定位 1.txt
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>
</span></span><span class="line"><span class="cl"><span class="n">Path</span> <span class="n">source</span> <span class="o">=</span> <span class="n">Paths</span><span class="o">.</span><span class="na">get</span><span class="o">(</span><span class="s">&#34;d:\\1.txt&#34;</span><span class="o">);</span> <span class="c1">// 绝对路径 代表了  d:\1.txt
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>
</span></span><span class="line"><span class="cl"><span class="n">Path</span> <span class="n">source</span> <span class="o">=</span> <span class="n">Paths</span><span class="o">.</span><span class="na">get</span><span class="o">(</span><span class="s">&#34;d:/1.txt&#34;</span><span class="o">);</span> <span class="c1">// 绝对路径 同样代表了  d:\1.txt
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>
</span></span><span class="line"><span class="cl"><span class="n">Path</span> <span class="n">projects</span> <span class="o">=</span> <span class="n">Paths</span><span class="o">.</span><span class="na">get</span><span class="o">(</span><span class="s">&#34;d:\\data&#34;</span><span class="o">,</span> <span class="s">&#34;projects&#34;</span><span class="o">);</span> <span class="c1">// 代表了  d:\data\projects
</span></span></span></code></pre></td></tr></table>
</div>
</div><ul>
<li><code>.</code> 代表了当前路径</li>
<li><code>..</code> 代表了上一级路径</li>
</ul>
<p>例如目录结构如下</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span><span class="lnt">2
</span><span class="lnt">3
</span><span class="lnt">4
</span><span class="lnt">5
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-fallback" data-lang="fallback"><span class="line"><span class="cl">d:
</span></span><span class="line"><span class="cl">	|- data
</span></span><span class="line"><span class="cl">		|- projects
</span></span><span class="line"><span class="cl">			|- a
</span></span><span class="line"><span class="cl">			|- b
</span></span></code></pre></td></tr></table>
</div>
</div><p>代码</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span><span class="lnt">2
</span><span class="lnt">3
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-java" data-lang="java"><span class="line"><span class="cl"><span class="n">Path</span> <span class="n">path</span> <span class="o">=</span> <span class="n">Paths</span><span class="o">.</span><span class="na">get</span><span class="o">(</span><span class="s">&#34;d:\\data\\projects\\a\\..\\b&#34;</span><span class="o">);</span>
</span></span><span class="line"><span class="cl"><span class="n">System</span><span class="o">.</span><span class="na">out</span><span class="o">.</span><span class="na">println</span><span class="o">(</span><span class="n">path</span><span class="o">);</span>
</span></span><span class="line"><span class="cl"><span class="n">System</span><span class="o">.</span><span class="na">out</span><span class="o">.</span><span class="na">println</span><span class="o">(</span><span class="n">path</span><span class="o">.</span><span class="na">normalize</span><span class="o">());</span> <span class="c1">// 正常化路径
</span></span></span></code></pre></td></tr></table>
</div>
</div><p>会输出</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span><span class="lnt">2
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-fallback" data-lang="fallback"><span class="line"><span class="cl">d:\data\projects\a\..\b
</span></span><span class="line"><span class="cl">d:\data\projects\b
</span></span></code></pre></td></tr></table>
</div>
</div><h3 id="34-files">3.4 Files</h3>
<p>检查文件是否存在</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span><span class="lnt">2
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-java" data-lang="java"><span class="line"><span class="cl"><span class="n">Path</span> <span class="n">path</span> <span class="o">=</span> <span class="n">Paths</span><span class="o">.</span><span class="na">get</span><span class="o">(</span><span class="s">&#34;helloword/data.txt&#34;</span><span class="o">);</span>
</span></span><span class="line"><span class="cl"><span class="n">System</span><span class="o">.</span><span class="na">out</span><span class="o">.</span><span class="na">println</span><span class="o">(</span><span class="n">Files</span><span class="o">.</span><span class="na">exists</span><span class="o">(</span><span class="n">path</span><span class="o">));</span>
</span></span></code></pre></td></tr></table>
</div>
</div><p>创建一级目录</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span><span class="lnt">2
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-java" data-lang="java"><span class="line"><span class="cl"><span class="n">Path</span> <span class="n">path</span> <span class="o">=</span> <span class="n">Paths</span><span class="o">.</span><span class="na">get</span><span class="o">(</span><span class="s">&#34;helloword/d1&#34;</span><span class="o">);</span>
</span></span><span class="line"><span class="cl"><span class="n">Files</span><span class="o">.</span><span class="na">createDirectory</span><span class="o">(</span><span class="n">path</span><span class="o">);</span>
</span></span></code></pre></td></tr></table>
</div>
</div><ul>
<li>如果目录已存在，会抛异常 FileAlreadyExistsException</li>
<li>不能一次创建多级目录，否则会抛异常 NoSuchFileException</li>
</ul>
<p>创建多级目录用</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span><span class="lnt">2
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-java" data-lang="java"><span class="line"><span class="cl"><span class="n">Path</span> <span class="n">path</span> <span class="o">=</span> <span class="n">Paths</span><span class="o">.</span><span class="na">get</span><span class="o">(</span><span class="s">&#34;helloword/d1/d2&#34;</span><span class="o">);</span>
</span></span><span class="line"><span class="cl"><span class="n">Files</span><span class="o">.</span><span class="na">createDirectories</span><span class="o">(</span><span class="n">path</span><span class="o">);</span>
</span></span></code></pre></td></tr></table>
</div>
</div><p>拷贝文件</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span><span class="lnt">2
</span><span class="lnt">3
</span><span class="lnt">4
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-java" data-lang="java"><span class="line"><span class="cl"><span class="n">Path</span> <span class="n">source</span> <span class="o">=</span> <span class="n">Paths</span><span class="o">.</span><span class="na">get</span><span class="o">(</span><span class="s">&#34;helloword/data.txt&#34;</span><span class="o">);</span>
</span></span><span class="line"><span class="cl"><span class="n">Path</span> <span class="n">target</span> <span class="o">=</span> <span class="n">Paths</span><span class="o">.</span><span class="na">get</span><span class="o">(</span><span class="s">&#34;helloword/target.txt&#34;</span><span class="o">);</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="n">Files</span><span class="o">.</span><span class="na">copy</span><span class="o">(</span><span class="n">source</span><span class="o">,</span> <span class="n">target</span><span class="o">);</span>
</span></span></code></pre></td></tr></table>
</div>
</div><ul>
<li>如果文件已存在，会抛异常 FileAlreadyExistsException</li>
</ul>
<p>如果希望用 source 覆盖掉 target，需要用 StandardCopyOption 来控制</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-java" data-lang="java"><span class="line"><span class="cl"><span class="n">Files</span><span class="o">.</span><span class="na">copy</span><span class="o">(</span><span class="n">source</span><span class="o">,</span> <span class="n">target</span><span class="o">,</span> <span class="n">StandardCopyOption</span><span class="o">.</span><span class="na">REPLACE_EXISTING</span><span class="o">);</span>
</span></span></code></pre></td></tr></table>
</div>
</div><p>移动文件</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span><span class="lnt">2
</span><span class="lnt">3
</span><span class="lnt">4
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-java" data-lang="java"><span class="line"><span class="cl"><span class="n">Path</span> <span class="n">source</span> <span class="o">=</span> <span class="n">Paths</span><span class="o">.</span><span class="na">get</span><span class="o">(</span><span class="s">&#34;helloword/data.txt&#34;</span><span class="o">);</span>
</span></span><span class="line"><span class="cl"><span class="n">Path</span> <span class="n">target</span> <span class="o">=</span> <span class="n">Paths</span><span class="o">.</span><span class="na">get</span><span class="o">(</span><span class="s">&#34;helloword/data.txt&#34;</span><span class="o">);</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="n">Files</span><span class="o">.</span><span class="na">move</span><span class="o">(</span><span class="n">source</span><span class="o">,</span> <span class="n">target</span><span class="o">,</span> <span class="n">StandardCopyOption</span><span class="o">.</span><span class="na">ATOMIC_MOVE</span><span class="o">);</span>
</span></span></code></pre></td></tr></table>
</div>
</div><ul>
<li>StandardCopyOption.ATOMIC_MOVE 保证文件移动的原子性</li>
</ul>
<p>删除文件</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span><span class="lnt">2
</span><span class="lnt">3
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-java" data-lang="java"><span class="line"><span class="cl"><span class="n">Path</span> <span class="n">target</span> <span class="o">=</span> <span class="n">Paths</span><span class="o">.</span><span class="na">get</span><span class="o">(</span><span class="s">&#34;helloword/target.txt&#34;</span><span class="o">);</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="n">Files</span><span class="o">.</span><span class="na">delete</span><span class="o">(</span><span class="n">target</span><span class="o">);</span>
</span></span></code></pre></td></tr></table>
</div>
</div><ul>
<li>如果文件不存在，会抛异常 NoSuchFileException</li>
</ul>
<p>删除目录</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span><span class="lnt">2
</span><span class="lnt">3
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-java" data-lang="java"><span class="line"><span class="cl"><span class="n">Path</span> <span class="n">target</span> <span class="o">=</span> <span class="n">Paths</span><span class="o">.</span><span class="na">get</span><span class="o">(</span><span class="s">&#34;helloword/d1&#34;</span><span class="o">);</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="n">Files</span><span class="o">.</span><span class="na">delete</span><span class="o">(</span><span class="n">target</span><span class="o">);</span>
</span></span></code></pre></td></tr></table>
</div>
</div><ul>
<li>如果目录还有内容，会抛异常 DirectoryNotEmptyException</li>
</ul>
<p>遍历目录文件</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt"> 1
</span><span class="lnt"> 2
</span><span class="lnt"> 3
</span><span class="lnt"> 4
</span><span class="lnt"> 5
</span><span class="lnt"> 6
</span><span class="lnt"> 7
</span><span class="lnt"> 8
</span><span class="lnt"> 9
</span><span class="lnt">10
</span><span class="lnt">11
</span><span class="lnt">12
</span><span class="lnt">13
</span><span class="lnt">14
</span><span class="lnt">15
</span><span class="lnt">16
</span><span class="lnt">17
</span><span class="lnt">18
</span><span class="lnt">19
</span><span class="lnt">20
</span><span class="lnt">21
</span><span class="lnt">22
</span><span class="lnt">23
</span><span class="lnt">24
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-java" data-lang="java"><span class="line"><span class="cl"><span class="kd">public</span> <span class="kd">static</span> <span class="kt">void</span> <span class="nf">main</span><span class="o">(</span><span class="n">String</span><span class="o">[]</span> <span class="n">args</span><span class="o">)</span> <span class="kd">throws</span> <span class="n">IOException</span> <span class="o">{</span>
</span></span><span class="line"><span class="cl">    <span class="n">Path</span> <span class="n">path</span> <span class="o">=</span> <span class="n">Paths</span><span class="o">.</span><span class="na">get</span><span class="o">(</span><span class="s">&#34;C:\\Program Files\\Java\\jdk1.8.0_91&#34;</span><span class="o">);</span>
</span></span><span class="line"><span class="cl">    <span class="n">AtomicInteger</span> <span class="n">dirCount</span> <span class="o">=</span> <span class="k">new</span> <span class="n">AtomicInteger</span><span class="o">();</span>
</span></span><span class="line"><span class="cl">    <span class="n">AtomicInteger</span> <span class="n">fileCount</span> <span class="o">=</span> <span class="k">new</span> <span class="n">AtomicInteger</span><span class="o">();</span>
</span></span><span class="line"><span class="cl">    <span class="n">Files</span><span class="o">.</span><span class="na">walkFileTree</span><span class="o">(</span><span class="n">path</span><span class="o">,</span> <span class="k">new</span> <span class="n">SimpleFileVisitor</span><span class="o">&lt;</span><span class="n">Path</span><span class="o">&gt;(){</span>
</span></span><span class="line"><span class="cl">        <span class="nd">@Override</span>
</span></span><span class="line"><span class="cl">        <span class="kd">public</span> <span class="n">FileVisitResult</span> <span class="nf">preVisitDirectory</span><span class="o">(</span><span class="n">Path</span> <span class="n">dir</span><span class="o">,</span> <span class="n">BasicFileAttributes</span> <span class="n">attrs</span><span class="o">)</span> 
</span></span><span class="line"><span class="cl">            <span class="kd">throws</span> <span class="n">IOException</span> <span class="o">{</span>
</span></span><span class="line"><span class="cl">            <span class="n">System</span><span class="o">.</span><span class="na">out</span><span class="o">.</span><span class="na">println</span><span class="o">(</span><span class="n">dir</span><span class="o">);</span>
</span></span><span class="line"><span class="cl">            <span class="n">dirCount</span><span class="o">.</span><span class="na">incrementAndGet</span><span class="o">();</span>
</span></span><span class="line"><span class="cl">            <span class="k">return</span> <span class="kd">super</span><span class="o">.</span><span class="na">preVisitDirectory</span><span class="o">(</span><span class="n">dir</span><span class="o">,</span> <span class="n">attrs</span><span class="o">);</span>
</span></span><span class="line"><span class="cl">        <span class="o">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">        <span class="nd">@Override</span>
</span></span><span class="line"><span class="cl">        <span class="kd">public</span> <span class="n">FileVisitResult</span> <span class="nf">visitFile</span><span class="o">(</span><span class="n">Path</span> <span class="n">file</span><span class="o">,</span> <span class="n">BasicFileAttributes</span> <span class="n">attrs</span><span class="o">)</span> 
</span></span><span class="line"><span class="cl">            <span class="kd">throws</span> <span class="n">IOException</span> <span class="o">{</span>
</span></span><span class="line"><span class="cl">            <span class="n">System</span><span class="o">.</span><span class="na">out</span><span class="o">.</span><span class="na">println</span><span class="o">(</span><span class="n">file</span><span class="o">);</span>
</span></span><span class="line"><span class="cl">            <span class="n">fileCount</span><span class="o">.</span><span class="na">incrementAndGet</span><span class="o">();</span>
</span></span><span class="line"><span class="cl">            <span class="k">return</span> <span class="kd">super</span><span class="o">.</span><span class="na">visitFile</span><span class="o">(</span><span class="n">file</span><span class="o">,</span> <span class="n">attrs</span><span class="o">);</span>
</span></span><span class="line"><span class="cl">        <span class="o">}</span>
</span></span><span class="line"><span class="cl">    <span class="o">});</span>
</span></span><span class="line"><span class="cl">    <span class="n">System</span><span class="o">.</span><span class="na">out</span><span class="o">.</span><span class="na">println</span><span class="o">(</span><span class="n">dirCount</span><span class="o">);</span> <span class="c1">// 133
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="n">System</span><span class="o">.</span><span class="na">out</span><span class="o">.</span><span class="na">println</span><span class="o">(</span><span class="n">fileCount</span><span class="o">);</span> <span class="c1">// 1479
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="o">}</span>
</span></span></code></pre></td></tr></table>
</div>
</div><p>统计 jar 的数目</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt"> 1
</span><span class="lnt"> 2
</span><span class="lnt"> 3
</span><span class="lnt"> 4
</span><span class="lnt"> 5
</span><span class="lnt"> 6
</span><span class="lnt"> 7
</span><span class="lnt"> 8
</span><span class="lnt"> 9
</span><span class="lnt">10
</span><span class="lnt">11
</span><span class="lnt">12
</span><span class="lnt">13
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-java" data-lang="java"><span class="line"><span class="cl"><span class="n">Path</span> <span class="n">path</span> <span class="o">=</span> <span class="n">Paths</span><span class="o">.</span><span class="na">get</span><span class="o">(</span><span class="s">&#34;C:\\Program Files\\Java\\jdk1.8.0_91&#34;</span><span class="o">);</span>
</span></span><span class="line"><span class="cl"><span class="n">AtomicInteger</span> <span class="n">fileCount</span> <span class="o">=</span> <span class="k">new</span> <span class="n">AtomicInteger</span><span class="o">();</span>
</span></span><span class="line"><span class="cl"><span class="n">Files</span><span class="o">.</span><span class="na">walkFileTree</span><span class="o">(</span><span class="n">path</span><span class="o">,</span> <span class="k">new</span> <span class="n">SimpleFileVisitor</span><span class="o">&lt;</span><span class="n">Path</span><span class="o">&gt;(){</span>
</span></span><span class="line"><span class="cl">    <span class="nd">@Override</span>
</span></span><span class="line"><span class="cl">    <span class="kd">public</span> <span class="n">FileVisitResult</span> <span class="nf">visitFile</span><span class="o">(</span><span class="n">Path</span> <span class="n">file</span><span class="o">,</span> <span class="n">BasicFileAttributes</span> <span class="n">attrs</span><span class="o">)</span> 
</span></span><span class="line"><span class="cl">        <span class="kd">throws</span> <span class="n">IOException</span> <span class="o">{</span>
</span></span><span class="line"><span class="cl">        <span class="k">if</span> <span class="o">(</span><span class="n">file</span><span class="o">.</span><span class="na">toFile</span><span class="o">().</span><span class="na">getName</span><span class="o">().</span><span class="na">endsWith</span><span class="o">(</span><span class="s">&#34;.jar&#34;</span><span class="o">))</span> <span class="o">{</span>
</span></span><span class="line"><span class="cl">            <span class="n">fileCount</span><span class="o">.</span><span class="na">incrementAndGet</span><span class="o">();</span>
</span></span><span class="line"><span class="cl">        <span class="o">}</span>
</span></span><span class="line"><span class="cl">        <span class="k">return</span> <span class="kd">super</span><span class="o">.</span><span class="na">visitFile</span><span class="o">(</span><span class="n">file</span><span class="o">,</span> <span class="n">attrs</span><span class="o">);</span>
</span></span><span class="line"><span class="cl">    <span class="o">}</span>
</span></span><span class="line"><span class="cl"><span class="o">});</span>
</span></span><span class="line"><span class="cl"><span class="n">System</span><span class="o">.</span><span class="na">out</span><span class="o">.</span><span class="na">println</span><span class="o">(</span><span class="n">fileCount</span><span class="o">);</span> <span class="c1">// 724
</span></span></span></code></pre></td></tr></table>
</div>
</div><p>删除多级目录</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt"> 1
</span><span class="lnt"> 2
</span><span class="lnt"> 3
</span><span class="lnt"> 4
</span><span class="lnt"> 5
</span><span class="lnt"> 6
</span><span class="lnt"> 7
</span><span class="lnt"> 8
</span><span class="lnt"> 9
</span><span class="lnt">10
</span><span class="lnt">11
</span><span class="lnt">12
</span><span class="lnt">13
</span><span class="lnt">14
</span><span class="lnt">15
</span><span class="lnt">16
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-java" data-lang="java"><span class="line"><span class="cl"><span class="n">Path</span> <span class="n">path</span> <span class="o">=</span> <span class="n">Paths</span><span class="o">.</span><span class="na">get</span><span class="o">(</span><span class="s">&#34;d:\\a&#34;</span><span class="o">);</span>
</span></span><span class="line"><span class="cl"><span class="n">Files</span><span class="o">.</span><span class="na">walkFileTree</span><span class="o">(</span><span class="n">path</span><span class="o">,</span> <span class="k">new</span> <span class="n">SimpleFileVisitor</span><span class="o">&lt;</span><span class="n">Path</span><span class="o">&gt;(){</span>
</span></span><span class="line"><span class="cl">    <span class="nd">@Override</span>
</span></span><span class="line"><span class="cl">    <span class="kd">public</span> <span class="n">FileVisitResult</span> <span class="nf">visitFile</span><span class="o">(</span><span class="n">Path</span> <span class="n">file</span><span class="o">,</span> <span class="n">BasicFileAttributes</span> <span class="n">attrs</span><span class="o">)</span> 
</span></span><span class="line"><span class="cl">        <span class="kd">throws</span> <span class="n">IOException</span> <span class="o">{</span>
</span></span><span class="line"><span class="cl">        <span class="n">Files</span><span class="o">.</span><span class="na">delete</span><span class="o">(</span><span class="n">file</span><span class="o">);</span>
</span></span><span class="line"><span class="cl">        <span class="k">return</span> <span class="kd">super</span><span class="o">.</span><span class="na">visitFile</span><span class="o">(</span><span class="n">file</span><span class="o">,</span> <span class="n">attrs</span><span class="o">);</span>
</span></span><span class="line"><span class="cl">    <span class="o">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="nd">@Override</span>
</span></span><span class="line"><span class="cl">    <span class="kd">public</span> <span class="n">FileVisitResult</span> <span class="nf">postVisitDirectory</span><span class="o">(</span><span class="n">Path</span> <span class="n">dir</span><span class="o">,</span> <span class="n">IOException</span> <span class="n">exc</span><span class="o">)</span> 
</span></span><span class="line"><span class="cl">        <span class="kd">throws</span> <span class="n">IOException</span> <span class="o">{</span>
</span></span><span class="line"><span class="cl">        <span class="n">Files</span><span class="o">.</span><span class="na">delete</span><span class="o">(</span><span class="n">dir</span><span class="o">);</span>
</span></span><span class="line"><span class="cl">        <span class="k">return</span> <span class="kd">super</span><span class="o">.</span><span class="na">postVisitDirectory</span><span class="o">(</span><span class="n">dir</span><span class="o">,</span> <span class="n">exc</span><span class="o">);</span>
</span></span><span class="line"><span class="cl">    <span class="o">}</span>
</span></span><span class="line"><span class="cl"><span class="o">});</span>
</span></span></code></pre></td></tr></table>
</div>
</div><h4 id="-删除很危险">⚠️ 删除很危险</h4>
<blockquote>
<p>删除是危险操作，确保要递归删除的文件夹没有重要内容</p>
</blockquote>
<p>拷贝多级目录</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt"> 1
</span><span class="lnt"> 2
</span><span class="lnt"> 3
</span><span class="lnt"> 4
</span><span class="lnt"> 5
</span><span class="lnt"> 6
</span><span class="lnt"> 7
</span><span class="lnt"> 8
</span><span class="lnt"> 9
</span><span class="lnt">10
</span><span class="lnt">11
</span><span class="lnt">12
</span><span class="lnt">13
</span><span class="lnt">14
</span><span class="lnt">15
</span><span class="lnt">16
</span><span class="lnt">17
</span><span class="lnt">18
</span><span class="lnt">19
</span><span class="lnt">20
</span><span class="lnt">21
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-java" data-lang="java"><span class="line"><span class="cl"><span class="kt">long</span> <span class="n">start</span> <span class="o">=</span> <span class="n">System</span><span class="o">.</span><span class="na">currentTimeMillis</span><span class="o">();</span>
</span></span><span class="line"><span class="cl"><span class="n">String</span> <span class="n">source</span> <span class="o">=</span> <span class="s">&#34;D:\\Snipaste-1.16.2-x64&#34;</span><span class="o">;</span>
</span></span><span class="line"><span class="cl"><span class="n">String</span> <span class="n">target</span> <span class="o">=</span> <span class="s">&#34;D:\\Snipaste-1.16.2-x64aaa&#34;</span><span class="o">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="n">Files</span><span class="o">.</span><span class="na">walk</span><span class="o">(</span><span class="n">Paths</span><span class="o">.</span><span class="na">get</span><span class="o">(</span><span class="n">source</span><span class="o">)).</span><span class="na">forEach</span><span class="o">(</span><span class="n">path</span> <span class="o">-&gt;</span> <span class="o">{</span>
</span></span><span class="line"><span class="cl">    <span class="k">try</span> <span class="o">{</span>
</span></span><span class="line"><span class="cl">        <span class="n">String</span> <span class="n">targetName</span> <span class="o">=</span> <span class="n">path</span><span class="o">.</span><span class="na">toString</span><span class="o">().</span><span class="na">replace</span><span class="o">(</span><span class="n">source</span><span class="o">,</span> <span class="n">target</span><span class="o">);</span>
</span></span><span class="line"><span class="cl">        <span class="c1">// 是目录
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>        <span class="k">if</span> <span class="o">(</span><span class="n">Files</span><span class="o">.</span><span class="na">isDirectory</span><span class="o">(</span><span class="n">path</span><span class="o">))</span> <span class="o">{</span>
</span></span><span class="line"><span class="cl">            <span class="n">Files</span><span class="o">.</span><span class="na">createDirectory</span><span class="o">(</span><span class="n">Paths</span><span class="o">.</span><span class="na">get</span><span class="o">(</span><span class="n">targetName</span><span class="o">));</span>
</span></span><span class="line"><span class="cl">        <span class="o">}</span>
</span></span><span class="line"><span class="cl">        <span class="c1">// 是普通文件
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>        <span class="k">else</span> <span class="k">if</span> <span class="o">(</span><span class="n">Files</span><span class="o">.</span><span class="na">isRegularFile</span><span class="o">(</span><span class="n">path</span><span class="o">))</span> <span class="o">{</span>
</span></span><span class="line"><span class="cl">            <span class="n">Files</span><span class="o">.</span><span class="na">copy</span><span class="o">(</span><span class="n">path</span><span class="o">,</span> <span class="n">Paths</span><span class="o">.</span><span class="na">get</span><span class="o">(</span><span class="n">targetName</span><span class="o">));</span>
</span></span><span class="line"><span class="cl">        <span class="o">}</span>
</span></span><span class="line"><span class="cl">    <span class="o">}</span> <span class="k">catch</span> <span class="o">(</span><span class="n">IOException</span> <span class="n">e</span><span class="o">)</span> <span class="o">{</span>
</span></span><span class="line"><span class="cl">        <span class="n">e</span><span class="o">.</span><span class="na">printStackTrace</span><span class="o">();</span>
</span></span><span class="line"><span class="cl">    <span class="o">}</span>
</span></span><span class="line"><span class="cl"><span class="o">});</span>
</span></span><span class="line"><span class="cl"><span class="kt">long</span> <span class="n">end</span> <span class="o">=</span> <span class="n">System</span><span class="o">.</span><span class="na">currentTimeMillis</span><span class="o">();</span>
</span></span><span class="line"><span class="cl"><span class="n">System</span><span class="o">.</span><span class="na">out</span><span class="o">.</span><span class="na">println</span><span class="o">(</span><span class="n">end</span> <span class="o">-</span> <span class="n">start</span><span class="o">);</span>
</span></span></code></pre></td></tr></table>
</div>
</div><h2 id="4-网络编程">4. 网络编程</h2>
<h3 id="41-非阻塞-vs-阻塞">4.1 非阻塞 vs 阻塞</h3>
<h4 id="阻塞">阻塞</h4>
<ul>
<li>阻塞模式下，相关方法都会导致线程暂停
<ul>
<li>ServerSocketChannel.accept 会在没有连接建立时让线程暂停</li>
<li>SocketChannel.read 会在没有数据可读时让线程暂停</li>
<li>阻塞的表现其实就是线程暂停了，暂停期间不会占用 cpu，但线程相当于闲置</li>
</ul>
</li>
<li>单线程下，阻塞方法之间相互影响，几乎不能正常工作，需要多线程支持</li>
<li>但多线程下，有新的问题，体现在以下方面
<ul>
<li>32 位 jvm 一个线程 320k，64 位 jvm 一个线程 1024k，如果连接数过多，必然导致 OOM，并且线程太多，反而会因为频繁上下文切换导致性能降低</li>
<li>可以采用线程池技术来减少线程数和线程上下文切换，但治标不治本，如果有很多连接建立，但长时间 inactive，会阻塞线程池中所有线程，因此不适合长连接，只适合短连接</li>
</ul>
</li>
</ul>
<p>服务器端</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt"> 1
</span><span class="lnt"> 2
</span><span class="lnt"> 3
</span><span class="lnt"> 4
</span><span class="lnt"> 5
</span><span class="lnt"> 6
</span><span class="lnt"> 7
</span><span class="lnt"> 8
</span><span class="lnt"> 9
</span><span class="lnt">10
</span><span class="lnt">11
</span><span class="lnt">12
</span><span class="lnt">13
</span><span class="lnt">14
</span><span class="lnt">15
</span><span class="lnt">16
</span><span class="lnt">17
</span><span class="lnt">18
</span><span class="lnt">19
</span><span class="lnt">20
</span><span class="lnt">21
</span><span class="lnt">22
</span><span class="lnt">23
</span><span class="lnt">24
</span><span class="lnt">25
</span><span class="lnt">26
</span><span class="lnt">27
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-java" data-lang="java"><span class="line"><span class="cl"><span class="c1">// 使用 nio 来理解阻塞模式, 单线程
</span></span></span><span class="line"><span class="cl"><span class="c1">// 0. ByteBuffer
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="n">ByteBuffer</span> <span class="n">buffer</span> <span class="o">=</span> <span class="n">ByteBuffer</span><span class="o">.</span><span class="na">allocate</span><span class="o">(</span><span class="n">16</span><span class="o">);</span>
</span></span><span class="line"><span class="cl"><span class="c1">// 1. 创建了服务器
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="n">ServerSocketChannel</span> <span class="n">ssc</span> <span class="o">=</span> <span class="n">ServerSocketChannel</span><span class="o">.</span><span class="na">open</span><span class="o">();</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">// 2. 绑定监听端口
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="n">ssc</span><span class="o">.</span><span class="na">bind</span><span class="o">(</span><span class="k">new</span> <span class="n">InetSocketAddress</span><span class="o">(</span><span class="n">8080</span><span class="o">));</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">// 3. 连接集合
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="n">List</span><span class="o">&lt;</span><span class="n">SocketChannel</span><span class="o">&gt;</span> <span class="n">channels</span> <span class="o">=</span> <span class="k">new</span> <span class="n">ArrayList</span><span class="o">&lt;&gt;();</span>
</span></span><span class="line"><span class="cl"><span class="k">while</span> <span class="o">(</span><span class="kc">true</span><span class="o">)</span> <span class="o">{</span>
</span></span><span class="line"><span class="cl">    <span class="c1">// 4. accept 建立与客户端连接， SocketChannel 用来与客户端之间通信
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="n">log</span><span class="o">.</span><span class="na">debug</span><span class="o">(</span><span class="s">&#34;connecting...&#34;</span><span class="o">);</span>
</span></span><span class="line"><span class="cl">    <span class="n">SocketChannel</span> <span class="n">sc</span> <span class="o">=</span> <span class="n">ssc</span><span class="o">.</span><span class="na">accept</span><span class="o">();</span> <span class="c1">// 阻塞方法，线程停止运行
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="n">log</span><span class="o">.</span><span class="na">debug</span><span class="o">(</span><span class="s">&#34;connected... {}&#34;</span><span class="o">,</span> <span class="n">sc</span><span class="o">);</span>
</span></span><span class="line"><span class="cl">    <span class="n">channels</span><span class="o">.</span><span class="na">add</span><span class="o">(</span><span class="n">sc</span><span class="o">);</span>
</span></span><span class="line"><span class="cl">    <span class="k">for</span> <span class="o">(</span><span class="n">SocketChannel</span> <span class="n">channel</span> <span class="o">:</span> <span class="n">channels</span><span class="o">)</span> <span class="o">{</span>
</span></span><span class="line"><span class="cl">        <span class="c1">// 5. 接收客户端发送的数据
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>        <span class="n">log</span><span class="o">.</span><span class="na">debug</span><span class="o">(</span><span class="s">&#34;before read... {}&#34;</span><span class="o">,</span> <span class="n">channel</span><span class="o">);</span>
</span></span><span class="line"><span class="cl">        <span class="n">channel</span><span class="o">.</span><span class="na">read</span><span class="o">(</span><span class="n">buffer</span><span class="o">);</span> <span class="c1">// 阻塞方法，线程停止运行
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>        <span class="n">buffer</span><span class="o">.</span><span class="na">flip</span><span class="o">();</span>
</span></span><span class="line"><span class="cl">        <span class="n">debugRead</span><span class="o">(</span><span class="n">buffer</span><span class="o">);</span>
</span></span><span class="line"><span class="cl">        <span class="n">buffer</span><span class="o">.</span><span class="na">clear</span><span class="o">();</span>
</span></span><span class="line"><span class="cl">        <span class="n">log</span><span class="o">.</span><span class="na">debug</span><span class="o">(</span><span class="s">&#34;after read...{}&#34;</span><span class="o">,</span> <span class="n">channel</span><span class="o">);</span>
</span></span><span class="line"><span class="cl">    <span class="o">}</span>
</span></span><span class="line"><span class="cl"><span class="o">}</span>
</span></span></code></pre></td></tr></table>
</div>
</div><p>客户端</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span><span class="lnt">2
</span><span class="lnt">3
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-java" data-lang="java"><span class="line"><span class="cl"><span class="n">SocketChannel</span> <span class="n">sc</span> <span class="o">=</span> <span class="n">SocketChannel</span><span class="o">.</span><span class="na">open</span><span class="o">();</span>
</span></span><span class="line"><span class="cl"><span class="n">sc</span><span class="o">.</span><span class="na">connect</span><span class="o">(</span><span class="k">new</span> <span class="n">InetSocketAddress</span><span class="o">(</span><span class="s">&#34;localhost&#34;</span><span class="o">,</span> <span class="n">8080</span><span class="o">));</span>
</span></span><span class="line"><span class="cl"><span class="n">System</span><span class="o">.</span><span class="na">out</span><span class="o">.</span><span class="na">println</span><span class="o">(</span><span class="s">&#34;waiting...&#34;</span><span class="o">);</span>
</span></span></code></pre></td></tr></table>
</div>
</div><h4 id="非阻塞">非阻塞</h4>
<ul>
<li>非阻塞模式下，相关方法都会不会让线程暂停
<ul>
<li>在 ServerSocketChannel.accept 在没有连接建立时，会返回 null，继续运行</li>
<li>SocketChannel.read 在没有数据可读时，会返回 0，但线程不必阻塞，可以去执行其它 SocketChannel 的 read 或是去执行 ServerSocketChannel.accept</li>
<li>写数据时，线程只是等待数据写入 Channel 即可，无需等 Channel 通过网络把数据发送出去</li>
</ul>
</li>
<li>但非阻塞模式下，即使没有连接建立，和可读数据，线程仍然在不断运行，白白浪费了 cpu</li>
<li>数据复制过程中，线程实际还是阻塞的（AIO 改进的地方）</li>
</ul>
<p>服务器端，客户端代码不变</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt"> 1
</span><span class="lnt"> 2
</span><span class="lnt"> 3
</span><span class="lnt"> 4
</span><span class="lnt"> 5
</span><span class="lnt"> 6
</span><span class="lnt"> 7
</span><span class="lnt"> 8
</span><span class="lnt"> 9
</span><span class="lnt">10
</span><span class="lnt">11
</span><span class="lnt">12
</span><span class="lnt">13
</span><span class="lnt">14
</span><span class="lnt">15
</span><span class="lnt">16
</span><span class="lnt">17
</span><span class="lnt">18
</span><span class="lnt">19
</span><span class="lnt">20
</span><span class="lnt">21
</span><span class="lnt">22
</span><span class="lnt">23
</span><span class="lnt">24
</span><span class="lnt">25
</span><span class="lnt">26
</span><span class="lnt">27
</span><span class="lnt">28
</span><span class="lnt">29
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-java" data-lang="java"><span class="line"><span class="cl"><span class="c1">// 使用 nio 来理解非阻塞模式, 单线程
</span></span></span><span class="line"><span class="cl"><span class="c1">// 0. ByteBuffer
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="n">ByteBuffer</span> <span class="n">buffer</span> <span class="o">=</span> <span class="n">ByteBuffer</span><span class="o">.</span><span class="na">allocate</span><span class="o">(</span><span class="n">16</span><span class="o">);</span>
</span></span><span class="line"><span class="cl"><span class="c1">// 1. 创建了服务器
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="n">ServerSocketChannel</span> <span class="n">ssc</span> <span class="o">=</span> <span class="n">ServerSocketChannel</span><span class="o">.</span><span class="na">open</span><span class="o">();</span>
</span></span><span class="line"><span class="cl"><span class="n">ssc</span><span class="o">.</span><span class="na">configureBlocking</span><span class="o">(</span><span class="kc">false</span><span class="o">);</span> <span class="c1">// 非阻塞模式
</span></span></span><span class="line"><span class="cl"><span class="c1">// 2. 绑定监听端口
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="n">ssc</span><span class="o">.</span><span class="na">bind</span><span class="o">(</span><span class="k">new</span> <span class="n">InetSocketAddress</span><span class="o">(</span><span class="n">8080</span><span class="o">));</span>
</span></span><span class="line"><span class="cl"><span class="c1">// 3. 连接集合
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="n">List</span><span class="o">&lt;</span><span class="n">SocketChannel</span><span class="o">&gt;</span> <span class="n">channels</span> <span class="o">=</span> <span class="k">new</span> <span class="n">ArrayList</span><span class="o">&lt;&gt;();</span>
</span></span><span class="line"><span class="cl"><span class="k">while</span> <span class="o">(</span><span class="kc">true</span><span class="o">)</span> <span class="o">{</span>
</span></span><span class="line"><span class="cl">    <span class="c1">// 4. accept 建立与客户端连接， SocketChannel 用来与客户端之间通信
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="n">SocketChannel</span> <span class="n">sc</span> <span class="o">=</span> <span class="n">ssc</span><span class="o">.</span><span class="na">accept</span><span class="o">();</span> <span class="c1">// 非阻塞，线程还会继续运行，如果没有连接建立，但sc是null
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="k">if</span> <span class="o">(</span><span class="n">sc</span> <span class="o">!=</span> <span class="kc">null</span><span class="o">)</span> <span class="o">{</span>
</span></span><span class="line"><span class="cl">        <span class="n">log</span><span class="o">.</span><span class="na">debug</span><span class="o">(</span><span class="s">&#34;connected... {}&#34;</span><span class="o">,</span> <span class="n">sc</span><span class="o">);</span>
</span></span><span class="line"><span class="cl">        <span class="n">sc</span><span class="o">.</span><span class="na">configureBlocking</span><span class="o">(</span><span class="kc">false</span><span class="o">);</span> <span class="c1">// 非阻塞模式
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>        <span class="n">channels</span><span class="o">.</span><span class="na">add</span><span class="o">(</span><span class="n">sc</span><span class="o">);</span>
</span></span><span class="line"><span class="cl">    <span class="o">}</span>
</span></span><span class="line"><span class="cl">    <span class="k">for</span> <span class="o">(</span><span class="n">SocketChannel</span> <span class="n">channel</span> <span class="o">:</span> <span class="n">channels</span><span class="o">)</span> <span class="o">{</span>
</span></span><span class="line"><span class="cl">        <span class="c1">// 5. 接收客户端发送的数据
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>        <span class="kt">int</span> <span class="n">read</span> <span class="o">=</span> <span class="n">channel</span><span class="o">.</span><span class="na">read</span><span class="o">(</span><span class="n">buffer</span><span class="o">);</span><span class="c1">// 非阻塞，线程仍然会继续运行，如果没有读到数据，read 返回 0
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>        <span class="k">if</span> <span class="o">(</span><span class="n">read</span> <span class="o">&gt;</span> <span class="n">0</span><span class="o">)</span> <span class="o">{</span>
</span></span><span class="line"><span class="cl">            <span class="n">buffer</span><span class="o">.</span><span class="na">flip</span><span class="o">();</span>
</span></span><span class="line"><span class="cl">            <span class="n">debugRead</span><span class="o">(</span><span class="n">buffer</span><span class="o">);</span>
</span></span><span class="line"><span class="cl">            <span class="n">buffer</span><span class="o">.</span><span class="na">clear</span><span class="o">();</span>
</span></span><span class="line"><span class="cl">            <span class="n">log</span><span class="o">.</span><span class="na">debug</span><span class="o">(</span><span class="s">&#34;after read...{}&#34;</span><span class="o">,</span> <span class="n">channel</span><span class="o">);</span>
</span></span><span class="line"><span class="cl">        <span class="o">}</span>
</span></span><span class="line"><span class="cl">    <span class="o">}</span>
</span></span><span class="line"><span class="cl"><span class="o">}</span>
</span></span></code></pre></td></tr></table>
</div>
</div><h4 id="多路复用">多路复用</h4>
<p>单线程可以配合 Selector 完成对多个 Channel 可读写事件的监控，这称之为多路复用</p>
<ul>
<li>多路复用仅针对网络 IO、普通文件 IO 没法利用多路复用</li>
<li>如果不用 Selector 的非阻塞模式，线程大部分时间都在做无用功，而 Selector 能够保证
<ul>
<li>有可连接事件时才去连接</li>
<li>有可读事件才去读取</li>
<li>有可写事件才去写入
<ul>
<li>限于网络传输能力，Channel 未必时时可写，一旦 Channel 可写，会触发 Selector 的可写事件</li>
</ul>
</li>
</ul>
</li>
</ul>
<h3 id="42-selector">4.2 Selector</h3>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span><span class="lnt">2
</span><span class="lnt">3
</span><span class="lnt">4
</span><span class="lnt">5
</span><span class="lnt">6
</span><span class="lnt">7
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-fallback" data-lang="fallback"><span class="line"><span class="cl">graph TD
</span></span><span class="line"><span class="cl">subgraph selector 版
</span></span><span class="line"><span class="cl">thread --&gt; selector
</span></span><span class="line"><span class="cl">selector --&gt; c1(channel)
</span></span><span class="line"><span class="cl">selector --&gt; c2(channel)
</span></span><span class="line"><span class="cl">selector --&gt; c3(channel)
</span></span><span class="line"><span class="cl">end
</span></span></code></pre></td></tr></table>
</div>
</div><p>好处</p>
<ul>
<li>一个线程配合 selector 就可以监控多个 channel 的事件，事件发生线程才去处理。避免非阻塞模式下所做无用功</li>
<li>让这个线程能够被充分利用</li>
<li>节约了线程的数量</li>
<li>减少了线程上下文切换</li>
</ul>
<h4 id="创建">创建</h4>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-java" data-lang="java"><span class="line"><span class="cl"><span class="n">Selector</span> <span class="n">selector</span> <span class="o">=</span> <span class="n">Selector</span><span class="o">.</span><span class="na">open</span><span class="o">();</span>
</span></span></code></pre></td></tr></table>
</div>
</div><h4 id="绑定-channel-事件">绑定 Channel 事件</h4>
<p>也称之为注册事件，绑定的事件 selector 才会关心</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span><span class="lnt">2
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-java" data-lang="java"><span class="line"><span class="cl"><span class="n">channel</span><span class="o">.</span><span class="na">configureBlocking</span><span class="o">(</span><span class="kc">false</span><span class="o">);</span>
</span></span><span class="line"><span class="cl"><span class="n">SelectionKey</span> <span class="n">key</span> <span class="o">=</span> <span class="n">channel</span><span class="o">.</span><span class="na">register</span><span class="o">(</span><span class="n">selector</span><span class="o">,</span> <span class="n">绑定事件</span><span class="o">);</span>
</span></span></code></pre></td></tr></table>
</div>
</div><ul>
<li>channel 必须工作在非阻塞模式</li>
<li>FileChannel 没有非阻塞模式，因此不能配合 selector 一起使用</li>
<li>绑定的事件类型可以有
<ul>
<li>connect - 客户端连接成功时触发</li>
<li>accept - 服务器端成功接受连接时触发</li>
<li>read - 数据可读入时触发，有因为接收能力弱，数据暂不能读入的情况</li>
<li>write - 数据可写出时触发，有因为发送能力弱，数据暂不能写出的情况</li>
</ul>
</li>
</ul>
<h4 id="监听-channel-事件">监听 Channel 事件</h4>
<p>可以通过下面三种方法来监听是否有事件发生，方法的返回值代表有多少 channel 发生了事件</p>
<p>方法1，阻塞直到绑定事件发生</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-java" data-lang="java"><span class="line"><span class="cl"><span class="kt">int</span> <span class="n">count</span> <span class="o">=</span> <span class="n">selector</span><span class="o">.</span><span class="na">select</span><span class="o">();</span>
</span></span></code></pre></td></tr></table>
</div>
</div><p>方法2，阻塞直到绑定事件发生，或是超时（时间单位为 ms）</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-java" data-lang="java"><span class="line"><span class="cl"><span class="kt">int</span> <span class="n">count</span> <span class="o">=</span> <span class="n">selector</span><span class="o">.</span><span class="na">select</span><span class="o">(</span><span class="kt">long</span> <span class="n">timeout</span><span class="o">);</span>
</span></span></code></pre></td></tr></table>
</div>
</div><p>方法3，不会阻塞，也就是不管有没有事件，立刻返回，自己根据返回值检查是否有事件</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-java" data-lang="java"><span class="line"><span class="cl"><span class="kt">int</span> <span class="n">count</span> <span class="o">=</span> <span class="n">selector</span><span class="o">.</span><span class="na">selectNow</span><span class="o">();</span>
</span></span></code></pre></td></tr></table>
</div>
</div><h4 id="-select-何时不阻塞">💡 select 何时不阻塞</h4>
<blockquote>
<ul>
<li>事件发生时
<ul>
<li>客户端发起连接请求，会触发 accept 事件</li>
<li>客户端发送数据过来，客户端正常、异常关闭时，都会触发 read 事件，另外如果发送的数据大于 buffer 缓冲区，会触发多次读取事件</li>
<li>channel 可写，会触发 write 事件</li>
<li>在 linux 下 nio bug 发生时</li>
</ul>
</li>
<li>调用 selector.wakeup()</li>
<li>调用 selector.close()</li>
<li>selector 所在线程 interrupt</li>
</ul>
</blockquote>
<h3 id="43-处理-accept-事件">4.3 处理 accept 事件</h3>
<p>客户端代码为</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt"> 1
</span><span class="lnt"> 2
</span><span class="lnt"> 3
</span><span class="lnt"> 4
</span><span class="lnt"> 5
</span><span class="lnt"> 6
</span><span class="lnt"> 7
</span><span class="lnt"> 8
</span><span class="lnt"> 9
</span><span class="lnt">10
</span><span class="lnt">11
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-java" data-lang="java"><span class="line"><span class="cl"><span class="kd">public</span> <span class="kd">class</span> <span class="nc">Client</span> <span class="o">{</span>
</span></span><span class="line"><span class="cl">    <span class="kd">public</span> <span class="kd">static</span> <span class="kt">void</span> <span class="nf">main</span><span class="o">(</span><span class="n">String</span><span class="o">[]</span> <span class="n">args</span><span class="o">)</span> <span class="o">{</span>
</span></span><span class="line"><span class="cl">        <span class="k">try</span> <span class="o">(</span><span class="n">Socket</span> <span class="n">socket</span> <span class="o">=</span> <span class="k">new</span> <span class="n">Socket</span><span class="o">(</span><span class="s">&#34;localhost&#34;</span><span class="o">,</span> <span class="n">8080</span><span class="o">))</span> <span class="o">{</span>
</span></span><span class="line"><span class="cl">            <span class="n">System</span><span class="o">.</span><span class="na">out</span><span class="o">.</span><span class="na">println</span><span class="o">(</span><span class="n">socket</span><span class="o">);</span>
</span></span><span class="line"><span class="cl">            <span class="n">socket</span><span class="o">.</span><span class="na">getOutputStream</span><span class="o">().</span><span class="na">write</span><span class="o">(</span><span class="s">&#34;world&#34;</span><span class="o">.</span><span class="na">getBytes</span><span class="o">());</span>
</span></span><span class="line"><span class="cl">            <span class="n">System</span><span class="o">.</span><span class="na">in</span><span class="o">.</span><span class="na">read</span><span class="o">();</span>
</span></span><span class="line"><span class="cl">        <span class="o">}</span> <span class="k">catch</span> <span class="o">(</span><span class="n">IOException</span> <span class="n">e</span><span class="o">)</span> <span class="o">{</span>
</span></span><span class="line"><span class="cl">            <span class="n">e</span><span class="o">.</span><span class="na">printStackTrace</span><span class="o">();</span>
</span></span><span class="line"><span class="cl">        <span class="o">}</span>
</span></span><span class="line"><span class="cl">    <span class="o">}</span>
</span></span><span class="line"><span class="cl"><span class="o">}</span>
</span></span></code></pre></td></tr></table>
</div>
</div><p>服务器端代码为</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt"> 1
</span><span class="lnt"> 2
</span><span class="lnt"> 3
</span><span class="lnt"> 4
</span><span class="lnt"> 5
</span><span class="lnt"> 6
</span><span class="lnt"> 7
</span><span class="lnt"> 8
</span><span class="lnt"> 9
</span><span class="lnt">10
</span><span class="lnt">11
</span><span class="lnt">12
</span><span class="lnt">13
</span><span class="lnt">14
</span><span class="lnt">15
</span><span class="lnt">16
</span><span class="lnt">17
</span><span class="lnt">18
</span><span class="lnt">19
</span><span class="lnt">20
</span><span class="lnt">21
</span><span class="lnt">22
</span><span class="lnt">23
</span><span class="lnt">24
</span><span class="lnt">25
</span><span class="lnt">26
</span><span class="lnt">27
</span><span class="lnt">28
</span><span class="lnt">29
</span><span class="lnt">30
</span><span class="lnt">31
</span><span class="lnt">32
</span><span class="lnt">33
</span><span class="lnt">34
</span><span class="lnt">35
</span><span class="lnt">36
</span><span class="lnt">37
</span><span class="lnt">38
</span><span class="lnt">39
</span><span class="lnt">40
</span><span class="lnt">41
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-java" data-lang="java"><span class="line"><span class="cl"><span class="nd">@Slf4j</span>
</span></span><span class="line"><span class="cl"><span class="kd">public</span> <span class="kd">class</span> <span class="nc">ChannelDemo6</span> <span class="o">{</span>
</span></span><span class="line"><span class="cl">    <span class="kd">public</span> <span class="kd">static</span> <span class="kt">void</span> <span class="nf">main</span><span class="o">(</span><span class="n">String</span><span class="o">[]</span> <span class="n">args</span><span class="o">)</span> <span class="o">{</span>
</span></span><span class="line"><span class="cl">        <span class="k">try</span> <span class="o">(</span><span class="n">ServerSocketChannel</span> <span class="n">channel</span> <span class="o">=</span> <span class="n">ServerSocketChannel</span><span class="o">.</span><span class="na">open</span><span class="o">())</span> <span class="o">{</span>
</span></span><span class="line"><span class="cl">            <span class="n">channel</span><span class="o">.</span><span class="na">bind</span><span class="o">(</span><span class="k">new</span> <span class="n">InetSocketAddress</span><span class="o">(</span><span class="n">8080</span><span class="o">));</span>
</span></span><span class="line"><span class="cl">            <span class="n">System</span><span class="o">.</span><span class="na">out</span><span class="o">.</span><span class="na">println</span><span class="o">(</span><span class="n">channel</span><span class="o">);</span>
</span></span><span class="line"><span class="cl">            <span class="n">Selector</span> <span class="n">selector</span> <span class="o">=</span> <span class="n">Selector</span><span class="o">.</span><span class="na">open</span><span class="o">();</span>
</span></span><span class="line"><span class="cl">            <span class="n">channel</span><span class="o">.</span><span class="na">configureBlocking</span><span class="o">(</span><span class="kc">false</span><span class="o">);</span>
</span></span><span class="line"><span class="cl">            <span class="n">channel</span><span class="o">.</span><span class="na">register</span><span class="o">(</span><span class="n">selector</span><span class="o">,</span> <span class="n">SelectionKey</span><span class="o">.</span><span class="na">OP_ACCEPT</span><span class="o">);</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">            <span class="k">while</span> <span class="o">(</span><span class="kc">true</span><span class="o">)</span> <span class="o">{</span>
</span></span><span class="line"><span class="cl">                <span class="kt">int</span> <span class="n">count</span> <span class="o">=</span> <span class="n">selector</span><span class="o">.</span><span class="na">select</span><span class="o">();</span>
</span></span><span class="line"><span class="cl"><span class="c1">//                int count = selector.selectNow();
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>                <span class="n">log</span><span class="o">.</span><span class="na">debug</span><span class="o">(</span><span class="s">&#34;select count: {}&#34;</span><span class="o">,</span> <span class="n">count</span><span class="o">);</span>
</span></span><span class="line"><span class="cl"><span class="c1">//                if(count &lt;= 0) {
</span></span></span><span class="line"><span class="cl"><span class="c1">//                    continue;
</span></span></span><span class="line"><span class="cl"><span class="c1">//                }
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>
</span></span><span class="line"><span class="cl">                <span class="c1">// 获取所有事件
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>                <span class="n">Set</span><span class="o">&lt;</span><span class="n">SelectionKey</span><span class="o">&gt;</span> <span class="n">keys</span> <span class="o">=</span> <span class="n">selector</span><span class="o">.</span><span class="na">selectedKeys</span><span class="o">();</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">                <span class="c1">// 遍历所有事件，逐一处理
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>                <span class="n">Iterator</span><span class="o">&lt;</span><span class="n">SelectionKey</span><span class="o">&gt;</span> <span class="n">iter</span> <span class="o">=</span> <span class="n">keys</span><span class="o">.</span><span class="na">iterator</span><span class="o">();</span>
</span></span><span class="line"><span class="cl">                <span class="k">while</span> <span class="o">(</span><span class="n">iter</span><span class="o">.</span><span class="na">hasNext</span><span class="o">())</span> <span class="o">{</span>
</span></span><span class="line"><span class="cl">                    <span class="n">SelectionKey</span> <span class="n">key</span> <span class="o">=</span> <span class="n">iter</span><span class="o">.</span><span class="na">next</span><span class="o">();</span>
</span></span><span class="line"><span class="cl">                    <span class="c1">// 判断事件类型
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>                    <span class="k">if</span> <span class="o">(</span><span class="n">key</span><span class="o">.</span><span class="na">isAcceptable</span><span class="o">())</span> <span class="o">{</span>
</span></span><span class="line"><span class="cl">                        <span class="n">ServerSocketChannel</span> <span class="n">c</span> <span class="o">=</span> <span class="o">(</span><span class="n">ServerSocketChannel</span><span class="o">)</span> <span class="n">key</span><span class="o">.</span><span class="na">channel</span><span class="o">();</span>
</span></span><span class="line"><span class="cl">                        <span class="c1">// 必须处理
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>                        <span class="n">SocketChannel</span> <span class="n">sc</span> <span class="o">=</span> <span class="n">c</span><span class="o">.</span><span class="na">accept</span><span class="o">();</span>
</span></span><span class="line"><span class="cl">                        <span class="n">log</span><span class="o">.</span><span class="na">debug</span><span class="o">(</span><span class="s">&#34;{}&#34;</span><span class="o">,</span> <span class="n">sc</span><span class="o">);</span>
</span></span><span class="line"><span class="cl">                    <span class="o">}</span>
</span></span><span class="line"><span class="cl">                    <span class="c1">// 处理完毕，必须将事件移除
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>                    <span class="n">iter</span><span class="o">.</span><span class="na">remove</span><span class="o">();</span>
</span></span><span class="line"><span class="cl">                <span class="o">}</span>
</span></span><span class="line"><span class="cl">            <span class="o">}</span>
</span></span><span class="line"><span class="cl">        <span class="o">}</span> <span class="k">catch</span> <span class="o">(</span><span class="n">IOException</span> <span class="n">e</span><span class="o">)</span> <span class="o">{</span>
</span></span><span class="line"><span class="cl">            <span class="n">e</span><span class="o">.</span><span class="na">printStackTrace</span><span class="o">();</span>
</span></span><span class="line"><span class="cl">        <span class="o">}</span>
</span></span><span class="line"><span class="cl">    <span class="o">}</span>
</span></span><span class="line"><span class="cl"><span class="o">}</span>
</span></span></code></pre></td></tr></table>
</div>
</div><h4 id="-事件发生后能否不处理">💡 事件发生后能否不处理</h4>
<blockquote>
<p>事件发生后，要么处理，要么取消（cancel），不能什么都不做，否则下次该事件仍会触发，这是因为 nio 底层使用的是水平触发</p>
</blockquote>
<h3 id="44-处理-read-事件">4.4 处理 read 事件</h3>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt"> 1
</span><span class="lnt"> 2
</span><span class="lnt"> 3
</span><span class="lnt"> 4
</span><span class="lnt"> 5
</span><span class="lnt"> 6
</span><span class="lnt"> 7
</span><span class="lnt"> 8
</span><span class="lnt"> 9
</span><span class="lnt">10
</span><span class="lnt">11
</span><span class="lnt">12
</span><span class="lnt">13
</span><span class="lnt">14
</span><span class="lnt">15
</span><span class="lnt">16
</span><span class="lnt">17
</span><span class="lnt">18
</span><span class="lnt">19
</span><span class="lnt">20
</span><span class="lnt">21
</span><span class="lnt">22
</span><span class="lnt">23
</span><span class="lnt">24
</span><span class="lnt">25
</span><span class="lnt">26
</span><span class="lnt">27
</span><span class="lnt">28
</span><span class="lnt">29
</span><span class="lnt">30
</span><span class="lnt">31
</span><span class="lnt">32
</span><span class="lnt">33
</span><span class="lnt">34
</span><span class="lnt">35
</span><span class="lnt">36
</span><span class="lnt">37
</span><span class="lnt">38
</span><span class="lnt">39
</span><span class="lnt">40
</span><span class="lnt">41
</span><span class="lnt">42
</span><span class="lnt">43
</span><span class="lnt">44
</span><span class="lnt">45
</span><span class="lnt">46
</span><span class="lnt">47
</span><span class="lnt">48
</span><span class="lnt">49
</span><span class="lnt">50
</span><span class="lnt">51
</span><span class="lnt">52
</span><span class="lnt">53
</span><span class="lnt">54
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-java" data-lang="java"><span class="line"><span class="cl"><span class="nd">@Slf4j</span>
</span></span><span class="line"><span class="cl"><span class="kd">public</span> <span class="kd">class</span> <span class="nc">ChannelDemo6</span> <span class="o">{</span>
</span></span><span class="line"><span class="cl">    <span class="kd">public</span> <span class="kd">static</span> <span class="kt">void</span> <span class="nf">main</span><span class="o">(</span><span class="n">String</span><span class="o">[]</span> <span class="n">args</span><span class="o">)</span> <span class="o">{</span>
</span></span><span class="line"><span class="cl">        <span class="k">try</span> <span class="o">(</span><span class="n">ServerSocketChannel</span> <span class="n">channel</span> <span class="o">=</span> <span class="n">ServerSocketChannel</span><span class="o">.</span><span class="na">open</span><span class="o">())</span> <span class="o">{</span>
</span></span><span class="line"><span class="cl">            <span class="n">channel</span><span class="o">.</span><span class="na">bind</span><span class="o">(</span><span class="k">new</span> <span class="n">InetSocketAddress</span><span class="o">(</span><span class="n">8080</span><span class="o">));</span>
</span></span><span class="line"><span class="cl">            <span class="n">System</span><span class="o">.</span><span class="na">out</span><span class="o">.</span><span class="na">println</span><span class="o">(</span><span class="n">channel</span><span class="o">);</span>
</span></span><span class="line"><span class="cl">            <span class="n">Selector</span> <span class="n">selector</span> <span class="o">=</span> <span class="n">Selector</span><span class="o">.</span><span class="na">open</span><span class="o">();</span>
</span></span><span class="line"><span class="cl">            <span class="n">channel</span><span class="o">.</span><span class="na">configureBlocking</span><span class="o">(</span><span class="kc">false</span><span class="o">);</span>
</span></span><span class="line"><span class="cl">            <span class="n">channel</span><span class="o">.</span><span class="na">register</span><span class="o">(</span><span class="n">selector</span><span class="o">,</span> <span class="n">SelectionKey</span><span class="o">.</span><span class="na">OP_ACCEPT</span><span class="o">);</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">            <span class="k">while</span> <span class="o">(</span><span class="kc">true</span><span class="o">)</span> <span class="o">{</span>
</span></span><span class="line"><span class="cl">                <span class="kt">int</span> <span class="n">count</span> <span class="o">=</span> <span class="n">selector</span><span class="o">.</span><span class="na">select</span><span class="o">();</span>
</span></span><span class="line"><span class="cl"><span class="c1">//                int count = selector.selectNow();
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>                <span class="n">log</span><span class="o">.</span><span class="na">debug</span><span class="o">(</span><span class="s">&#34;select count: {}&#34;</span><span class="o">,</span> <span class="n">count</span><span class="o">);</span>
</span></span><span class="line"><span class="cl"><span class="c1">//                if(count &lt;= 0) {
</span></span></span><span class="line"><span class="cl"><span class="c1">//                    continue;
</span></span></span><span class="line"><span class="cl"><span class="c1">//                }
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>
</span></span><span class="line"><span class="cl">                <span class="c1">// 获取所有事件
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>                <span class="n">Set</span><span class="o">&lt;</span><span class="n">SelectionKey</span><span class="o">&gt;</span> <span class="n">keys</span> <span class="o">=</span> <span class="n">selector</span><span class="o">.</span><span class="na">selectedKeys</span><span class="o">();</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">                <span class="c1">// 遍历所有事件，逐一处理
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>                <span class="n">Iterator</span><span class="o">&lt;</span><span class="n">SelectionKey</span><span class="o">&gt;</span> <span class="n">iter</span> <span class="o">=</span> <span class="n">keys</span><span class="o">.</span><span class="na">iterator</span><span class="o">();</span>
</span></span><span class="line"><span class="cl">                <span class="k">while</span> <span class="o">(</span><span class="n">iter</span><span class="o">.</span><span class="na">hasNext</span><span class="o">())</span> <span class="o">{</span>
</span></span><span class="line"><span class="cl">                    <span class="n">SelectionKey</span> <span class="n">key</span> <span class="o">=</span> <span class="n">iter</span><span class="o">.</span><span class="na">next</span><span class="o">();</span>
</span></span><span class="line"><span class="cl">                    <span class="c1">// 判断事件类型
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>                    <span class="k">if</span> <span class="o">(</span><span class="n">key</span><span class="o">.</span><span class="na">isAcceptable</span><span class="o">())</span> <span class="o">{</span>
</span></span><span class="line"><span class="cl">                        <span class="n">ServerSocketChannel</span> <span class="n">c</span> <span class="o">=</span> <span class="o">(</span><span class="n">ServerSocketChannel</span><span class="o">)</span> <span class="n">key</span><span class="o">.</span><span class="na">channel</span><span class="o">();</span>
</span></span><span class="line"><span class="cl">                        <span class="c1">// 必须处理
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>                        <span class="n">SocketChannel</span> <span class="n">sc</span> <span class="o">=</span> <span class="n">c</span><span class="o">.</span><span class="na">accept</span><span class="o">();</span>
</span></span><span class="line"><span class="cl">                        <span class="n">sc</span><span class="o">.</span><span class="na">configureBlocking</span><span class="o">(</span><span class="kc">false</span><span class="o">);</span>
</span></span><span class="line"><span class="cl">                        <span class="n">sc</span><span class="o">.</span><span class="na">register</span><span class="o">(</span><span class="n">selector</span><span class="o">,</span> <span class="n">SelectionKey</span><span class="o">.</span><span class="na">OP_READ</span><span class="o">);</span>
</span></span><span class="line"><span class="cl">                        <span class="n">log</span><span class="o">.</span><span class="na">debug</span><span class="o">(</span><span class="s">&#34;连接已建立: {}&#34;</span><span class="o">,</span> <span class="n">sc</span><span class="o">);</span>
</span></span><span class="line"><span class="cl">                    <span class="o">}</span> <span class="k">else</span> <span class="k">if</span> <span class="o">(</span><span class="n">key</span><span class="o">.</span><span class="na">isReadable</span><span class="o">())</span> <span class="o">{</span>
</span></span><span class="line"><span class="cl">                        <span class="n">SocketChannel</span> <span class="n">sc</span> <span class="o">=</span> <span class="o">(</span><span class="n">SocketChannel</span><span class="o">)</span> <span class="n">key</span><span class="o">.</span><span class="na">channel</span><span class="o">();</span>
</span></span><span class="line"><span class="cl">                        <span class="n">ByteBuffer</span> <span class="n">buffer</span> <span class="o">=</span> <span class="n">ByteBuffer</span><span class="o">.</span><span class="na">allocate</span><span class="o">(</span><span class="n">128</span><span class="o">);</span>
</span></span><span class="line"><span class="cl">                        <span class="kt">int</span> <span class="n">read</span> <span class="o">=</span> <span class="n">sc</span><span class="o">.</span><span class="na">read</span><span class="o">(</span><span class="n">buffer</span><span class="o">);</span>
</span></span><span class="line"><span class="cl">                        <span class="k">if</span><span class="o">(</span><span class="n">read</span> <span class="o">==</span> <span class="o">-</span><span class="n">1</span><span class="o">)</span> <span class="o">{</span>
</span></span><span class="line"><span class="cl">                            <span class="n">key</span><span class="o">.</span><span class="na">cancel</span><span class="o">();</span>
</span></span><span class="line"><span class="cl">                            <span class="n">sc</span><span class="o">.</span><span class="na">close</span><span class="o">();</span>
</span></span><span class="line"><span class="cl">                        <span class="o">}</span> <span class="k">else</span> <span class="o">{</span>
</span></span><span class="line"><span class="cl">                            <span class="n">buffer</span><span class="o">.</span><span class="na">flip</span><span class="o">();</span>
</span></span><span class="line"><span class="cl">                            <span class="n">debug</span><span class="o">(</span><span class="n">buffer</span><span class="o">);</span>
</span></span><span class="line"><span class="cl">                        <span class="o">}</span>
</span></span><span class="line"><span class="cl">                    <span class="o">}</span>
</span></span><span class="line"><span class="cl">                    <span class="c1">// 处理完毕，必须将事件移除
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>                    <span class="n">iter</span><span class="o">.</span><span class="na">remove</span><span class="o">();</span>
</span></span><span class="line"><span class="cl">                <span class="o">}</span>
</span></span><span class="line"><span class="cl">            <span class="o">}</span>
</span></span><span class="line"><span class="cl">        <span class="o">}</span> <span class="k">catch</span> <span class="o">(</span><span class="n">IOException</span> <span class="n">e</span><span class="o">)</span> <span class="o">{</span>
</span></span><span class="line"><span class="cl">            <span class="n">e</span><span class="o">.</span><span class="na">printStackTrace</span><span class="o">();</span>
</span></span><span class="line"><span class="cl">        <span class="o">}</span>
</span></span><span class="line"><span class="cl">    <span class="o">}</span>
</span></span><span class="line"><span class="cl"><span class="o">}</span>
</span></span></code></pre></td></tr></table>
</div>
</div><p>开启两个客户端，修改一下发送文字，输出</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt"> 1
</span><span class="lnt"> 2
</span><span class="lnt"> 3
</span><span class="lnt"> 4
</span><span class="lnt"> 5
</span><span class="lnt"> 6
</span><span class="lnt"> 7
</span><span class="lnt"> 8
</span><span class="lnt"> 9
</span><span class="lnt">10
</span><span class="lnt">11
</span><span class="lnt">12
</span><span class="lnt">13
</span><span class="lnt">14
</span><span class="lnt">15
</span><span class="lnt">16
</span><span class="lnt">17
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-fallback" data-lang="fallback"><span class="line"><span class="cl">sun.nio.ch.ServerSocketChannelImpl[/0:0:0:0:0:0:0:0:8080]
</span></span><span class="line"><span class="cl">21:16:39 [DEBUG] [main] c.i.n.ChannelDemo6 - select count: 1
</span></span><span class="line"><span class="cl">21:16:39 [DEBUG] [main] c.i.n.ChannelDemo6 - 连接已建立: java.nio.channels.SocketChannel[connected local=/127.0.0.1:8080 remote=/127.0.0.1:60367]
</span></span><span class="line"><span class="cl">21:16:39 [DEBUG] [main] c.i.n.ChannelDemo6 - select count: 1
</span></span><span class="line"><span class="cl">         +-------------------------------------------------+
</span></span><span class="line"><span class="cl">         |  0  1  2  3  4  5  6  7  8  9  a  b  c  d  e  f |
</span></span><span class="line"><span class="cl">+--------+-------------------------------------------------+----------------+
</span></span><span class="line"><span class="cl">|00000000| 68 65 6c 6c 6f                                  |hello           |
</span></span><span class="line"><span class="cl">+--------+-------------------------------------------------+----------------+
</span></span><span class="line"><span class="cl">21:16:59 [DEBUG] [main] c.i.n.ChannelDemo6 - select count: 1
</span></span><span class="line"><span class="cl">21:16:59 [DEBUG] [main] c.i.n.ChannelDemo6 - 连接已建立: java.nio.channels.SocketChannel[connected local=/127.0.0.1:8080 remote=/127.0.0.1:60378]
</span></span><span class="line"><span class="cl">21:16:59 [DEBUG] [main] c.i.n.ChannelDemo6 - select count: 1
</span></span><span class="line"><span class="cl">         +-------------------------------------------------+
</span></span><span class="line"><span class="cl">         |  0  1  2  3  4  5  6  7  8  9  a  b  c  d  e  f |
</span></span><span class="line"><span class="cl">+--------+-------------------------------------------------+----------------+
</span></span><span class="line"><span class="cl">|00000000| 77 6f 72 6c 64                                  |world           |
</span></span><span class="line"><span class="cl">+--------+-------------------------------------------------+----------------+
</span></span></code></pre></td></tr></table>
</div>
</div><h4 id="-为何要-iterremove">💡 为何要 iter.remove()</h4>
<blockquote>
<p>因为 select 在事件发生后，就会将相关的 key 放入 selectedKeys 集合，但不会在处理完后从 selectedKeys 集合中移除，需要我们自己编码删除。例如</p>
<ul>
<li>第一次触发了 ssckey 上的 accept 事件，没有移除 ssckey</li>
<li>第二次触发了 sckey 上的 read 事件，但这时 selectedKeys 中还有上次的 ssckey ，在处理时因为没有真正的 serverSocket 连上了，就会导致空指针异常</li>
</ul>
</blockquote>
<h4 id="-cancel-的作用">💡 cancel 的作用</h4>
<blockquote>
<p>cancel 会取消注册在 selector 上的 channel，并从 keys 集合中删除 key 后续不会再监听事件</p>
</blockquote>
<h4 id="--不处理边界的问题">⚠️  不处理边界的问题</h4>
<p>以前有同学写过这样的代码，思考注释中两个问题，以 bio 为例，其实 nio 道理是一样的</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt"> 1
</span><span class="lnt"> 2
</span><span class="lnt"> 3
</span><span class="lnt"> 4
</span><span class="lnt"> 5
</span><span class="lnt"> 6
</span><span class="lnt"> 7
</span><span class="lnt"> 8
</span><span class="lnt"> 9
</span><span class="lnt">10
</span><span class="lnt">11
</span><span class="lnt">12
</span><span class="lnt">13
</span><span class="lnt">14
</span><span class="lnt">15
</span><span class="lnt">16
</span><span class="lnt">17
</span><span class="lnt">18
</span><span class="lnt">19
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-java" data-lang="java"><span class="line"><span class="cl"><span class="kd">public</span> <span class="kd">class</span> <span class="nc">Server</span> <span class="o">{</span>
</span></span><span class="line"><span class="cl">    <span class="kd">public</span> <span class="kd">static</span> <span class="kt">void</span> <span class="nf">main</span><span class="o">(</span><span class="n">String</span><span class="o">[]</span> <span class="n">args</span><span class="o">)</span> <span class="kd">throws</span> <span class="n">IOException</span> <span class="o">{</span>
</span></span><span class="line"><span class="cl">        <span class="n">ServerSocket</span> <span class="n">ss</span><span class="o">=</span><span class="k">new</span> <span class="n">ServerSocket</span><span class="o">(</span><span class="n">9000</span><span class="o">);</span>
</span></span><span class="line"><span class="cl">        <span class="k">while</span> <span class="o">(</span><span class="kc">true</span><span class="o">)</span> <span class="o">{</span>
</span></span><span class="line"><span class="cl">            <span class="n">Socket</span> <span class="n">s</span> <span class="o">=</span> <span class="n">ss</span><span class="o">.</span><span class="na">accept</span><span class="o">();</span>
</span></span><span class="line"><span class="cl">            <span class="n">InputStream</span> <span class="n">in</span> <span class="o">=</span> <span class="n">s</span><span class="o">.</span><span class="na">getInputStream</span><span class="o">();</span>
</span></span><span class="line"><span class="cl">            <span class="c1">// 这里这么写，有没有问题
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>            <span class="kt">byte</span><span class="o">[]</span> <span class="n">arr</span> <span class="o">=</span> <span class="k">new</span> <span class="kt">byte</span><span class="o">[</span><span class="n">4</span><span class="o">];</span>
</span></span><span class="line"><span class="cl">            <span class="k">while</span><span class="o">(</span><span class="kc">true</span><span class="o">)</span> <span class="o">{</span>
</span></span><span class="line"><span class="cl">                <span class="kt">int</span> <span class="n">read</span> <span class="o">=</span> <span class="n">in</span><span class="o">.</span><span class="na">read</span><span class="o">(</span><span class="n">arr</span><span class="o">);</span>
</span></span><span class="line"><span class="cl">                <span class="c1">// 这里这么写，有没有问题
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>                <span class="k">if</span><span class="o">(</span><span class="n">read</span> <span class="o">==</span> <span class="o">-</span><span class="n">1</span><span class="o">)</span> <span class="o">{</span>
</span></span><span class="line"><span class="cl">                    <span class="k">break</span><span class="o">;</span>
</span></span><span class="line"><span class="cl">                <span class="o">}</span>
</span></span><span class="line"><span class="cl">                <span class="n">System</span><span class="o">.</span><span class="na">out</span><span class="o">.</span><span class="na">println</span><span class="o">(</span><span class="k">new</span> <span class="n">String</span><span class="o">(</span><span class="n">arr</span><span class="o">,</span> <span class="n">0</span><span class="o">,</span> <span class="n">read</span><span class="o">));</span>
</span></span><span class="line"><span class="cl">            <span class="o">}</span>
</span></span><span class="line"><span class="cl">        <span class="o">}</span>
</span></span><span class="line"><span class="cl">    <span class="o">}</span>
</span></span><span class="line"><span class="cl"><span class="o">}</span>
</span></span></code></pre></td></tr></table>
</div>
</div><p>客户端</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt"> 1
</span><span class="lnt"> 2
</span><span class="lnt"> 3
</span><span class="lnt"> 4
</span><span class="lnt"> 5
</span><span class="lnt"> 6
</span><span class="lnt"> 7
</span><span class="lnt"> 8
</span><span class="lnt"> 9
</span><span class="lnt">10
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-java" data-lang="java"><span class="line"><span class="cl"><span class="kd">public</span> <span class="kd">class</span> <span class="nc">Client</span> <span class="o">{</span>
</span></span><span class="line"><span class="cl">    <span class="kd">public</span> <span class="kd">static</span> <span class="kt">void</span> <span class="nf">main</span><span class="o">(</span><span class="n">String</span><span class="o">[]</span> <span class="n">args</span><span class="o">)</span> <span class="kd">throws</span> <span class="n">IOException</span> <span class="o">{</span>
</span></span><span class="line"><span class="cl">        <span class="n">Socket</span> <span class="n">max</span> <span class="o">=</span> <span class="k">new</span> <span class="n">Socket</span><span class="o">(</span><span class="s">&#34;localhost&#34;</span><span class="o">,</span> <span class="n">9000</span><span class="o">);</span>
</span></span><span class="line"><span class="cl">        <span class="n">OutputStream</span> <span class="n">out</span> <span class="o">=</span> <span class="n">max</span><span class="o">.</span><span class="na">getOutputStream</span><span class="o">();</span>
</span></span><span class="line"><span class="cl">        <span class="n">out</span><span class="o">.</span><span class="na">write</span><span class="o">(</span><span class="s">&#34;hello&#34;</span><span class="o">.</span><span class="na">getBytes</span><span class="o">());</span>
</span></span><span class="line"><span class="cl">        <span class="n">out</span><span class="o">.</span><span class="na">write</span><span class="o">(</span><span class="s">&#34;world&#34;</span><span class="o">.</span><span class="na">getBytes</span><span class="o">());</span>
</span></span><span class="line"><span class="cl">        <span class="n">out</span><span class="o">.</span><span class="na">write</span><span class="o">(</span><span class="s">&#34;你好&#34;</span><span class="o">.</span><span class="na">getBytes</span><span class="o">());</span>
</span></span><span class="line"><span class="cl">        <span class="n">max</span><span class="o">.</span><span class="na">close</span><span class="o">();</span>
</span></span><span class="line"><span class="cl">    <span class="o">}</span>
</span></span><span class="line"><span class="cl"><span class="o">}</span>
</span></span></code></pre></td></tr></table>
</div>
</div><p>输出</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span><span class="lnt">2
</span><span class="lnt">3
</span><span class="lnt">4
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-fallback" data-lang="fallback"><span class="line"><span class="cl">hell
</span></span><span class="line"><span class="cl">owor
</span></span><span class="line"><span class="cl">ld�
</span></span><span class="line"><span class="cl">�好
</span></span></code></pre></td></tr></table>
</div>
</div><p>为什么？</p>
<h4 id="处理消息的边界">处理消息的边界</h4>
<p><img
        class="lazyload"
        src="/svg/loading.min.svg"
        data-src="/img/0023.png"
        data-srcset="/img/0023.png, /img/0023.png 1.5x, /img/0023.png 2x"
        data-sizes="auto"
        alt="/img/0023.png"
        title="/img/0023.png" /></p>
<ul>
<li>一种思路是固定消息长度，数据包大小一样，服务器按预定长度读取，缺点是浪费带宽</li>
<li>另一种思路是按分隔符拆分，缺点是效率低</li>
<li>TLV 格式，即 Type 类型、Length 长度、Value 数据，类型和长度已知的情况下，就可以方便获取消息大小，分配合适的 buffer，缺点是 buffer 需要提前分配，如果内容过大，则影响 server 吞吐量
<ul>
<li>Http 1.1 是 TLV 格式</li>
<li>Http 2.0 是 LTV 格式</li>
</ul>
</li>
</ul>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt"> 1
</span><span class="lnt"> 2
</span><span class="lnt"> 3
</span><span class="lnt"> 4
</span><span class="lnt"> 5
</span><span class="lnt"> 6
</span><span class="lnt"> 7
</span><span class="lnt"> 8
</span><span class="lnt"> 9
</span><span class="lnt">10
</span><span class="lnt">11
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-fallback" data-lang="fallback"><span class="line"><span class="cl">sequenceDiagram 
</span></span><span class="line"><span class="cl">participant c1 as 客户端1
</span></span><span class="line"><span class="cl">participant s as 服务器
</span></span><span class="line"><span class="cl">participant b1 as ByteBuffer1
</span></span><span class="line"><span class="cl">participant b2 as ByteBuffer2
</span></span><span class="line"><span class="cl">c1 -&gt;&gt; s: 发送 01234567890abcdef3333\r
</span></span><span class="line"><span class="cl">s -&gt;&gt; b1: 第一次 read 存入 01234567890abcdef
</span></span><span class="line"><span class="cl">s -&gt;&gt; b2: 扩容
</span></span><span class="line"><span class="cl">b1 -&gt;&gt; b2: 拷贝 01234567890abcdef
</span></span><span class="line"><span class="cl">s -&gt;&gt; b2: 第二次 read 存入 3333\r
</span></span><span class="line"><span class="cl">b2 -&gt;&gt; b2: 01234567890abcdef3333\r
</span></span></code></pre></td></tr></table>
</div>
</div><p>服务器端</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt"> 1
</span><span class="lnt"> 2
</span><span class="lnt"> 3
</span><span class="lnt"> 4
</span><span class="lnt"> 5
</span><span class="lnt"> 6
</span><span class="lnt"> 7
</span><span class="lnt"> 8
</span><span class="lnt"> 9
</span><span class="lnt">10
</span><span class="lnt">11
</span><span class="lnt">12
</span><span class="lnt">13
</span><span class="lnt">14
</span><span class="lnt">15
</span><span class="lnt">16
</span><span class="lnt">17
</span><span class="lnt">18
</span><span class="lnt">19
</span><span class="lnt">20
</span><span class="lnt">21
</span><span class="lnt">22
</span><span class="lnt">23
</span><span class="lnt">24
</span><span class="lnt">25
</span><span class="lnt">26
</span><span class="lnt">27
</span><span class="lnt">28
</span><span class="lnt">29
</span><span class="lnt">30
</span><span class="lnt">31
</span><span class="lnt">32
</span><span class="lnt">33
</span><span class="lnt">34
</span><span class="lnt">35
</span><span class="lnt">36
</span><span class="lnt">37
</span><span class="lnt">38
</span><span class="lnt">39
</span><span class="lnt">40
</span><span class="lnt">41
</span><span class="lnt">42
</span><span class="lnt">43
</span><span class="lnt">44
</span><span class="lnt">45
</span><span class="lnt">46
</span><span class="lnt">47
</span><span class="lnt">48
</span><span class="lnt">49
</span><span class="lnt">50
</span><span class="lnt">51
</span><span class="lnt">52
</span><span class="lnt">53
</span><span class="lnt">54
</span><span class="lnt">55
</span><span class="lnt">56
</span><span class="lnt">57
</span><span class="lnt">58
</span><span class="lnt">59
</span><span class="lnt">60
</span><span class="lnt">61
</span><span class="lnt">62
</span><span class="lnt">63
</span><span class="lnt">64
</span><span class="lnt">65
</span><span class="lnt">66
</span><span class="lnt">67
</span><span class="lnt">68
</span><span class="lnt">69
</span><span class="lnt">70
</span><span class="lnt">71
</span><span class="lnt">72
</span><span class="lnt">73
</span><span class="lnt">74
</span><span class="lnt">75
</span><span class="lnt">76
</span><span class="lnt">77
</span><span class="lnt">78
</span><span class="lnt">79
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-java" data-lang="java"><span class="line"><span class="cl"><span class="kd">private</span> <span class="kd">static</span> <span class="kt">void</span> <span class="nf">split</span><span class="o">(</span><span class="n">ByteBuffer</span> <span class="n">source</span><span class="o">)</span> <span class="o">{</span>
</span></span><span class="line"><span class="cl">    <span class="n">source</span><span class="o">.</span><span class="na">flip</span><span class="o">();</span>
</span></span><span class="line"><span class="cl">    <span class="k">for</span> <span class="o">(</span><span class="kt">int</span> <span class="n">i</span> <span class="o">=</span> <span class="n">0</span><span class="o">;</span> <span class="n">i</span> <span class="o">&lt;</span> <span class="n">source</span><span class="o">.</span><span class="na">limit</span><span class="o">();</span> <span class="n">i</span><span class="o">++)</span> <span class="o">{</span>
</span></span><span class="line"><span class="cl">        <span class="c1">// 找到一条完整消息
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>        <span class="k">if</span> <span class="o">(</span><span class="n">source</span><span class="o">.</span><span class="na">get</span><span class="o">(</span><span class="n">i</span><span class="o">)</span> <span class="o">==</span> <span class="sc">&#39;\n&#39;</span><span class="o">)</span> <span class="o">{</span>
</span></span><span class="line"><span class="cl">            <span class="kt">int</span> <span class="n">length</span> <span class="o">=</span> <span class="n">i</span> <span class="o">+</span> <span class="n">1</span> <span class="o">-</span> <span class="n">source</span><span class="o">.</span><span class="na">position</span><span class="o">();</span>
</span></span><span class="line"><span class="cl">            <span class="c1">// 把这条完整消息存入新的 ByteBuffer
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>            <span class="n">ByteBuffer</span> <span class="n">target</span> <span class="o">=</span> <span class="n">ByteBuffer</span><span class="o">.</span><span class="na">allocate</span><span class="o">(</span><span class="n">length</span><span class="o">);</span>
</span></span><span class="line"><span class="cl">            <span class="c1">// 从 source 读，向 target 写
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>            <span class="k">for</span> <span class="o">(</span><span class="kt">int</span> <span class="n">j</span> <span class="o">=</span> <span class="n">0</span><span class="o">;</span> <span class="n">j</span> <span class="o">&lt;</span> <span class="n">length</span><span class="o">;</span> <span class="n">j</span><span class="o">++)</span> <span class="o">{</span>
</span></span><span class="line"><span class="cl">                <span class="n">target</span><span class="o">.</span><span class="na">put</span><span class="o">(</span><span class="n">source</span><span class="o">.</span><span class="na">get</span><span class="o">());</span>
</span></span><span class="line"><span class="cl">            <span class="o">}</span>
</span></span><span class="line"><span class="cl">            <span class="n">debugAll</span><span class="o">(</span><span class="n">target</span><span class="o">);</span>
</span></span><span class="line"><span class="cl">        <span class="o">}</span>
</span></span><span class="line"><span class="cl">    <span class="o">}</span>
</span></span><span class="line"><span class="cl">    <span class="n">source</span><span class="o">.</span><span class="na">compact</span><span class="o">();</span> <span class="c1">// 0123456789abcdef  position 16 limit 16
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="o">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kd">public</span> <span class="kd">static</span> <span class="kt">void</span> <span class="nf">main</span><span class="o">(</span><span class="n">String</span><span class="o">[]</span> <span class="n">args</span><span class="o">)</span> <span class="kd">throws</span> <span class="n">IOException</span> <span class="o">{</span>
</span></span><span class="line"><span class="cl">    <span class="c1">// 1. 创建 selector, 管理多个 channel
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="n">Selector</span> <span class="n">selector</span> <span class="o">=</span> <span class="n">Selector</span><span class="o">.</span><span class="na">open</span><span class="o">();</span>
</span></span><span class="line"><span class="cl">    <span class="n">ServerSocketChannel</span> <span class="n">ssc</span> <span class="o">=</span> <span class="n">ServerSocketChannel</span><span class="o">.</span><span class="na">open</span><span class="o">();</span>
</span></span><span class="line"><span class="cl">    <span class="n">ssc</span><span class="o">.</span><span class="na">configureBlocking</span><span class="o">(</span><span class="kc">false</span><span class="o">);</span>
</span></span><span class="line"><span class="cl">    <span class="c1">// 2. 建立 selector 和 channel 的联系（注册）
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="c1">// SelectionKey 就是将来事件发生后，通过它可以知道事件和哪个channel的事件
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="n">SelectionKey</span> <span class="n">sscKey</span> <span class="o">=</span> <span class="n">ssc</span><span class="o">.</span><span class="na">register</span><span class="o">(</span><span class="n">selector</span><span class="o">,</span> <span class="n">0</span><span class="o">,</span> <span class="kc">null</span><span class="o">);</span>
</span></span><span class="line"><span class="cl">    <span class="c1">// key 只关注 accept 事件
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="n">sscKey</span><span class="o">.</span><span class="na">interestOps</span><span class="o">(</span><span class="n">SelectionKey</span><span class="o">.</span><span class="na">OP_ACCEPT</span><span class="o">);</span>
</span></span><span class="line"><span class="cl">    <span class="n">log</span><span class="o">.</span><span class="na">debug</span><span class="o">(</span><span class="s">&#34;sscKey:{}&#34;</span><span class="o">,</span> <span class="n">sscKey</span><span class="o">);</span>
</span></span><span class="line"><span class="cl">    <span class="n">ssc</span><span class="o">.</span><span class="na">bind</span><span class="o">(</span><span class="k">new</span> <span class="n">InetSocketAddress</span><span class="o">(</span><span class="n">8080</span><span class="o">));</span>
</span></span><span class="line"><span class="cl">    <span class="k">while</span> <span class="o">(</span><span class="kc">true</span><span class="o">)</span> <span class="o">{</span>
</span></span><span class="line"><span class="cl">        <span class="c1">// 3. select 方法, 没有事件发生，线程阻塞，有事件，线程才会恢复运行
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>        <span class="c1">// select 在事件未处理时，它不会阻塞, 事件发生后要么处理，要么取消，不能置之不理
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>        <span class="n">selector</span><span class="o">.</span><span class="na">select</span><span class="o">();</span>
</span></span><span class="line"><span class="cl">        <span class="c1">// 4. 处理事件, selectedKeys 内部包含了所有发生的事件
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>        <span class="n">Iterator</span><span class="o">&lt;</span><span class="n">SelectionKey</span><span class="o">&gt;</span> <span class="n">iter</span> <span class="o">=</span> <span class="n">selector</span><span class="o">.</span><span class="na">selectedKeys</span><span class="o">().</span><span class="na">iterator</span><span class="o">();</span> <span class="c1">// accept, read
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>        <span class="k">while</span> <span class="o">(</span><span class="n">iter</span><span class="o">.</span><span class="na">hasNext</span><span class="o">())</span> <span class="o">{</span>
</span></span><span class="line"><span class="cl">            <span class="n">SelectionKey</span> <span class="n">key</span> <span class="o">=</span> <span class="n">iter</span><span class="o">.</span><span class="na">next</span><span class="o">();</span>
</span></span><span class="line"><span class="cl">            <span class="c1">// 处理key 时，要从 selectedKeys 集合中删除，否则下次处理就会有问题
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>            <span class="n">iter</span><span class="o">.</span><span class="na">remove</span><span class="o">();</span>
</span></span><span class="line"><span class="cl">            <span class="n">log</span><span class="o">.</span><span class="na">debug</span><span class="o">(</span><span class="s">&#34;key: {}&#34;</span><span class="o">,</span> <span class="n">key</span><span class="o">);</span>
</span></span><span class="line"><span class="cl">            <span class="c1">// 5. 区分事件类型
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>            <span class="k">if</span> <span class="o">(</span><span class="n">key</span><span class="o">.</span><span class="na">isAcceptable</span><span class="o">())</span> <span class="o">{</span> <span class="c1">// 如果是 accept
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>                <span class="n">ServerSocketChannel</span> <span class="n">channel</span> <span class="o">=</span> <span class="o">(</span><span class="n">ServerSocketChannel</span><span class="o">)</span> <span class="n">key</span><span class="o">.</span><span class="na">channel</span><span class="o">();</span>
</span></span><span class="line"><span class="cl">                <span class="n">SocketChannel</span> <span class="n">sc</span> <span class="o">=</span> <span class="n">channel</span><span class="o">.</span><span class="na">accept</span><span class="o">();</span>
</span></span><span class="line"><span class="cl">                <span class="n">sc</span><span class="o">.</span><span class="na">configureBlocking</span><span class="o">(</span><span class="kc">false</span><span class="o">);</span>
</span></span><span class="line"><span class="cl">                <span class="n">ByteBuffer</span> <span class="n">buffer</span> <span class="o">=</span> <span class="n">ByteBuffer</span><span class="o">.</span><span class="na">allocate</span><span class="o">(</span><span class="n">16</span><span class="o">);</span> <span class="c1">// attachment
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>                <span class="c1">// 将一个 byteBuffer 作为附件关联到 selectionKey 上
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>                <span class="n">SelectionKey</span> <span class="n">scKey</span> <span class="o">=</span> <span class="n">sc</span><span class="o">.</span><span class="na">register</span><span class="o">(</span><span class="n">selector</span><span class="o">,</span> <span class="n">0</span><span class="o">,</span> <span class="n">buffer</span><span class="o">);</span>
</span></span><span class="line"><span class="cl">                <span class="n">scKey</span><span class="o">.</span><span class="na">interestOps</span><span class="o">(</span><span class="n">SelectionKey</span><span class="o">.</span><span class="na">OP_READ</span><span class="o">);</span>
</span></span><span class="line"><span class="cl">                <span class="n">log</span><span class="o">.</span><span class="na">debug</span><span class="o">(</span><span class="s">&#34;{}&#34;</span><span class="o">,</span> <span class="n">sc</span><span class="o">);</span>
</span></span><span class="line"><span class="cl">                <span class="n">log</span><span class="o">.</span><span class="na">debug</span><span class="o">(</span><span class="s">&#34;scKey:{}&#34;</span><span class="o">,</span> <span class="n">scKey</span><span class="o">);</span>
</span></span><span class="line"><span class="cl">            <span class="o">}</span> <span class="k">else</span> <span class="k">if</span> <span class="o">(</span><span class="n">key</span><span class="o">.</span><span class="na">isReadable</span><span class="o">())</span> <span class="o">{</span> <span class="c1">// 如果是 read
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>                <span class="k">try</span> <span class="o">{</span>
</span></span><span class="line"><span class="cl">                    <span class="n">SocketChannel</span> <span class="n">channel</span> <span class="o">=</span> <span class="o">(</span><span class="n">SocketChannel</span><span class="o">)</span> <span class="n">key</span><span class="o">.</span><span class="na">channel</span><span class="o">();</span> <span class="c1">// 拿到触发事件的channel
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>                    <span class="c1">// 获取 selectionKey 上关联的附件
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>                    <span class="n">ByteBuffer</span> <span class="n">buffer</span> <span class="o">=</span> <span class="o">(</span><span class="n">ByteBuffer</span><span class="o">)</span> <span class="n">key</span><span class="o">.</span><span class="na">attachment</span><span class="o">();</span>
</span></span><span class="line"><span class="cl">                    <span class="kt">int</span> <span class="n">read</span> <span class="o">=</span> <span class="n">channel</span><span class="o">.</span><span class="na">read</span><span class="o">(</span><span class="n">buffer</span><span class="o">);</span> <span class="c1">// 如果是正常断开，read 的方法的返回值是 -1
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>                    <span class="k">if</span><span class="o">(</span><span class="n">read</span> <span class="o">==</span> <span class="o">-</span><span class="n">1</span><span class="o">)</span> <span class="o">{</span>
</span></span><span class="line"><span class="cl">                        <span class="n">key</span><span class="o">.</span><span class="na">cancel</span><span class="o">();</span>
</span></span><span class="line"><span class="cl">                    <span class="o">}</span> <span class="k">else</span> <span class="o">{</span>
</span></span><span class="line"><span class="cl">                        <span class="n">split</span><span class="o">(</span><span class="n">buffer</span><span class="o">);</span>
</span></span><span class="line"><span class="cl">                        <span class="c1">// 需要扩容
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>                        <span class="k">if</span> <span class="o">(</span><span class="n">buffer</span><span class="o">.</span><span class="na">position</span><span class="o">()</span> <span class="o">==</span> <span class="n">buffer</span><span class="o">.</span><span class="na">limit</span><span class="o">())</span> <span class="o">{</span>
</span></span><span class="line"><span class="cl">                            <span class="n">ByteBuffer</span> <span class="n">newBuffer</span> <span class="o">=</span> <span class="n">ByteBuffer</span><span class="o">.</span><span class="na">allocate</span><span class="o">(</span><span class="n">buffer</span><span class="o">.</span><span class="na">capacity</span><span class="o">()</span> <span class="o">*</span> <span class="n">2</span><span class="o">);</span>
</span></span><span class="line"><span class="cl">                            <span class="n">buffer</span><span class="o">.</span><span class="na">flip</span><span class="o">();</span>
</span></span><span class="line"><span class="cl">                            <span class="n">newBuffer</span><span class="o">.</span><span class="na">put</span><span class="o">(</span><span class="n">buffer</span><span class="o">);</span> <span class="c1">// 0123456789abcdef3333\n
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>                            <span class="n">key</span><span class="o">.</span><span class="na">attach</span><span class="o">(</span><span class="n">newBuffer</span><span class="o">);</span>
</span></span><span class="line"><span class="cl">                        <span class="o">}</span>
</span></span><span class="line"><span class="cl">                    <span class="o">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">                <span class="o">}</span> <span class="k">catch</span> <span class="o">(</span><span class="n">IOException</span> <span class="n">e</span><span class="o">)</span> <span class="o">{</span>
</span></span><span class="line"><span class="cl">                    <span class="n">e</span><span class="o">.</span><span class="na">printStackTrace</span><span class="o">();</span>
</span></span><span class="line"><span class="cl">                    <span class="n">key</span><span class="o">.</span><span class="na">cancel</span><span class="o">();</span>  <span class="c1">// 因为客户端断开了,因此需要将 key 取消（从 selector 的 keys 集合中真正删除 key）
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>                <span class="o">}</span>
</span></span><span class="line"><span class="cl">            <span class="o">}</span>
</span></span><span class="line"><span class="cl">        <span class="o">}</span>
</span></span><span class="line"><span class="cl">    <span class="o">}</span>
</span></span><span class="line"><span class="cl"><span class="o">}</span>
</span></span></code></pre></td></tr></table>
</div>
</div><p>客户端</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span><span class="lnt">2
</span><span class="lnt">3
</span><span class="lnt">4
</span><span class="lnt">5
</span><span class="lnt">6
</span><span class="lnt">7
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-java" data-lang="java"><span class="line"><span class="cl"><span class="n">SocketChannel</span> <span class="n">sc</span> <span class="o">=</span> <span class="n">SocketChannel</span><span class="o">.</span><span class="na">open</span><span class="o">();</span>
</span></span><span class="line"><span class="cl"><span class="n">sc</span><span class="o">.</span><span class="na">connect</span><span class="o">(</span><span class="k">new</span> <span class="n">InetSocketAddress</span><span class="o">(</span><span class="s">&#34;localhost&#34;</span><span class="o">,</span> <span class="n">8080</span><span class="o">));</span>
</span></span><span class="line"><span class="cl"><span class="n">SocketAddress</span> <span class="n">address</span> <span class="o">=</span> <span class="n">sc</span><span class="o">.</span><span class="na">getLocalAddress</span><span class="o">();</span>
</span></span><span class="line"><span class="cl"><span class="c1">// sc.write(Charset.defaultCharset().encode(&#34;hello\nworld\n&#34;));
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="n">sc</span><span class="o">.</span><span class="na">write</span><span class="o">(</span><span class="n">Charset</span><span class="o">.</span><span class="na">defaultCharset</span><span class="o">().</span><span class="na">encode</span><span class="o">(</span><span class="s">&#34;0123\n456789abcdef&#34;</span><span class="o">));</span>
</span></span><span class="line"><span class="cl"><span class="n">sc</span><span class="o">.</span><span class="na">write</span><span class="o">(</span><span class="n">Charset</span><span class="o">.</span><span class="na">defaultCharset</span><span class="o">().</span><span class="na">encode</span><span class="o">(</span><span class="s">&#34;0123456789abcdef3333\n&#34;</span><span class="o">));</span>
</span></span><span class="line"><span class="cl"><span class="n">System</span><span class="o">.</span><span class="na">in</span><span class="o">.</span><span class="na">read</span><span class="o">();</span>
</span></span></code></pre></td></tr></table>
</div>
</div><h4 id="bytebuffer-大小分配">ByteBuffer 大小分配</h4>
<ul>
<li>每个 channel 都需要记录可能被切分的消息，因为 ByteBuffer 不能被多个 channel 共同使用，因此需要为每个 channel 维护一个独立的 ByteBuffer</li>
<li>ByteBuffer 不能太大，比如一个 ByteBuffer 1Mb 的话，要支持百万连接就要 1Tb 内存，因此需要设计大小可变的 ByteBuffer
<ul>
<li>一种思路是首先分配一个较小的 buffer，例如 4k，如果发现数据不够，再分配 8k 的 buffer，将 4k buffer 内容拷贝至 8k buffer，优点是消息连续容易处理，缺点是数据拷贝耗费性能，参考实现 <a href="http://tutorials.jenkov.com/java-performance/resizable-array.html" target="_blank" rel="noopener noreffer">http://tutorials.jenkov.com/java-performance/resizable-array.html</a></li>
<li>另一种思路是用多个数组组成 buffer，一个数组不够，把多出来的内容写入新的数组，与前面的区别是消息存储不连续解析复杂，优点是避免了拷贝引起的性能损耗</li>
</ul>
</li>
</ul>
<h3 id="45-处理-write-事件">4.5 处理 write 事件</h3>
<h4 id="一次无法写完例子">一次无法写完例子</h4>
<ul>
<li>非阻塞模式下，无法保证把 buffer 中所有数据都写入 channel，因此需要追踪 write 方法的返回值（代表实际写入字节数）</li>
<li>用 selector 监听所有 channel 的可写事件，每个 channel 都需要一个 key 来跟踪 buffer，但这样又会导致占用内存过多，就有两阶段策略
<ul>
<li>当消息处理器第一次写入消息时，才将 channel 注册到 selector 上</li>
<li>selector 检查 channel 上的可写事件，如果所有的数据写完了，就取消 channel 的注册</li>
<li>如果不取消，会每次可写均会触发 write 事件</li>
</ul>
</li>
</ul>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt"> 1
</span><span class="lnt"> 2
</span><span class="lnt"> 3
</span><span class="lnt"> 4
</span><span class="lnt"> 5
</span><span class="lnt"> 6
</span><span class="lnt"> 7
</span><span class="lnt"> 8
</span><span class="lnt"> 9
</span><span class="lnt">10
</span><span class="lnt">11
</span><span class="lnt">12
</span><span class="lnt">13
</span><span class="lnt">14
</span><span class="lnt">15
</span><span class="lnt">16
</span><span class="lnt">17
</span><span class="lnt">18
</span><span class="lnt">19
</span><span class="lnt">20
</span><span class="lnt">21
</span><span class="lnt">22
</span><span class="lnt">23
</span><span class="lnt">24
</span><span class="lnt">25
</span><span class="lnt">26
</span><span class="lnt">27
</span><span class="lnt">28
</span><span class="lnt">29
</span><span class="lnt">30
</span><span class="lnt">31
</span><span class="lnt">32
</span><span class="lnt">33
</span><span class="lnt">34
</span><span class="lnt">35
</span><span class="lnt">36
</span><span class="lnt">37
</span><span class="lnt">38
</span><span class="lnt">39
</span><span class="lnt">40
</span><span class="lnt">41
</span><span class="lnt">42
</span><span class="lnt">43
</span><span class="lnt">44
</span><span class="lnt">45
</span><span class="lnt">46
</span><span class="lnt">47
</span><span class="lnt">48
</span><span class="lnt">49
</span><span class="lnt">50
</span><span class="lnt">51
</span><span class="lnt">52
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-java" data-lang="java"><span class="line"><span class="cl"><span class="kd">public</span> <span class="kd">class</span> <span class="nc">WriteServer</span> <span class="o">{</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="kd">public</span> <span class="kd">static</span> <span class="kt">void</span> <span class="nf">main</span><span class="o">(</span><span class="n">String</span><span class="o">[]</span> <span class="n">args</span><span class="o">)</span> <span class="kd">throws</span> <span class="n">IOException</span> <span class="o">{</span>
</span></span><span class="line"><span class="cl">        <span class="n">ServerSocketChannel</span> <span class="n">ssc</span> <span class="o">=</span> <span class="n">ServerSocketChannel</span><span class="o">.</span><span class="na">open</span><span class="o">();</span>
</span></span><span class="line"><span class="cl">        <span class="n">ssc</span><span class="o">.</span><span class="na">configureBlocking</span><span class="o">(</span><span class="kc">false</span><span class="o">);</span>
</span></span><span class="line"><span class="cl">        <span class="n">ssc</span><span class="o">.</span><span class="na">bind</span><span class="o">(</span><span class="k">new</span> <span class="n">InetSocketAddress</span><span class="o">(</span><span class="n">8080</span><span class="o">));</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">        <span class="n">Selector</span> <span class="n">selector</span> <span class="o">=</span> <span class="n">Selector</span><span class="o">.</span><span class="na">open</span><span class="o">();</span>
</span></span><span class="line"><span class="cl">        <span class="n">ssc</span><span class="o">.</span><span class="na">register</span><span class="o">(</span><span class="n">selector</span><span class="o">,</span> <span class="n">SelectionKey</span><span class="o">.</span><span class="na">OP_ACCEPT</span><span class="o">);</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">        <span class="k">while</span><span class="o">(</span><span class="kc">true</span><span class="o">)</span> <span class="o">{</span>
</span></span><span class="line"><span class="cl">            <span class="n">selector</span><span class="o">.</span><span class="na">select</span><span class="o">();</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">            <span class="n">Iterator</span><span class="o">&lt;</span><span class="n">SelectionKey</span><span class="o">&gt;</span> <span class="n">iter</span> <span class="o">=</span> <span class="n">selector</span><span class="o">.</span><span class="na">selectedKeys</span><span class="o">().</span><span class="na">iterator</span><span class="o">();</span>
</span></span><span class="line"><span class="cl">            <span class="k">while</span> <span class="o">(</span><span class="n">iter</span><span class="o">.</span><span class="na">hasNext</span><span class="o">())</span> <span class="o">{</span>
</span></span><span class="line"><span class="cl">                <span class="n">SelectionKey</span> <span class="n">key</span> <span class="o">=</span> <span class="n">iter</span><span class="o">.</span><span class="na">next</span><span class="o">();</span>
</span></span><span class="line"><span class="cl">                <span class="n">iter</span><span class="o">.</span><span class="na">remove</span><span class="o">();</span>
</span></span><span class="line"><span class="cl">                <span class="k">if</span> <span class="o">(</span><span class="n">key</span><span class="o">.</span><span class="na">isAcceptable</span><span class="o">())</span> <span class="o">{</span>
</span></span><span class="line"><span class="cl">                    <span class="n">SocketChannel</span> <span class="n">sc</span> <span class="o">=</span> <span class="n">ssc</span><span class="o">.</span><span class="na">accept</span><span class="o">();</span>
</span></span><span class="line"><span class="cl">                    <span class="n">sc</span><span class="o">.</span><span class="na">configureBlocking</span><span class="o">(</span><span class="kc">false</span><span class="o">);</span>
</span></span><span class="line"><span class="cl">                    <span class="n">SelectionKey</span> <span class="n">sckey</span> <span class="o">=</span> <span class="n">sc</span><span class="o">.</span><span class="na">register</span><span class="o">(</span><span class="n">selector</span><span class="o">,</span> <span class="n">SelectionKey</span><span class="o">.</span><span class="na">OP_READ</span><span class="o">);</span>
</span></span><span class="line"><span class="cl">                    <span class="c1">// 1. 向客户端发送内容
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>                    <span class="n">StringBuilder</span> <span class="n">sb</span> <span class="o">=</span> <span class="k">new</span> <span class="n">StringBuilder</span><span class="o">();</span>
</span></span><span class="line"><span class="cl">                    <span class="k">for</span> <span class="o">(</span><span class="kt">int</span> <span class="n">i</span> <span class="o">=</span> <span class="n">0</span><span class="o">;</span> <span class="n">i</span> <span class="o">&lt;</span> <span class="n">3000000</span><span class="o">;</span> <span class="n">i</span><span class="o">++)</span> <span class="o">{</span>
</span></span><span class="line"><span class="cl">                        <span class="n">sb</span><span class="o">.</span><span class="na">append</span><span class="o">(</span><span class="s">&#34;a&#34;</span><span class="o">);</span>
</span></span><span class="line"><span class="cl">                    <span class="o">}</span>
</span></span><span class="line"><span class="cl">                    <span class="n">ByteBuffer</span> <span class="n">buffer</span> <span class="o">=</span> <span class="n">Charset</span><span class="o">.</span><span class="na">defaultCharset</span><span class="o">().</span><span class="na">encode</span><span class="o">(</span><span class="n">sb</span><span class="o">.</span><span class="na">toString</span><span class="o">());</span>
</span></span><span class="line"><span class="cl">                    <span class="kt">int</span> <span class="n">write</span> <span class="o">=</span> <span class="n">sc</span><span class="o">.</span><span class="na">write</span><span class="o">(</span><span class="n">buffer</span><span class="o">);</span>
</span></span><span class="line"><span class="cl">                    <span class="c1">// 3. write 表示实际写了多少字节
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>                    <span class="n">System</span><span class="o">.</span><span class="na">out</span><span class="o">.</span><span class="na">println</span><span class="o">(</span><span class="s">&#34;实际写入字节:&#34;</span> <span class="o">+</span> <span class="n">write</span><span class="o">);</span>
</span></span><span class="line"><span class="cl">                    <span class="c1">// 4. 如果有剩余未读字节，才需要关注写事件
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>                    <span class="k">if</span> <span class="o">(</span><span class="n">buffer</span><span class="o">.</span><span class="na">hasRemaining</span><span class="o">())</span> <span class="o">{</span>
</span></span><span class="line"><span class="cl">                        <span class="c1">// read 1  write 4
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>                        <span class="c1">// 在原有关注事件的基础上，多关注 写事件
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>                        <span class="n">sckey</span><span class="o">.</span><span class="na">interestOps</span><span class="o">(</span><span class="n">sckey</span><span class="o">.</span><span class="na">interestOps</span><span class="o">()</span> <span class="o">+</span> <span class="n">SelectionKey</span><span class="o">.</span><span class="na">OP_WRITE</span><span class="o">);</span>
</span></span><span class="line"><span class="cl">                        <span class="c1">// 把 buffer 作为附件加入 sckey
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>                        <span class="n">sckey</span><span class="o">.</span><span class="na">attach</span><span class="o">(</span><span class="n">buffer</span><span class="o">);</span>
</span></span><span class="line"><span class="cl">                    <span class="o">}</span>
</span></span><span class="line"><span class="cl">                <span class="o">}</span> <span class="k">else</span> <span class="k">if</span> <span class="o">(</span><span class="n">key</span><span class="o">.</span><span class="na">isWritable</span><span class="o">())</span> <span class="o">{</span>
</span></span><span class="line"><span class="cl">                    <span class="n">ByteBuffer</span> <span class="n">buffer</span> <span class="o">=</span> <span class="o">(</span><span class="n">ByteBuffer</span><span class="o">)</span> <span class="n">key</span><span class="o">.</span><span class="na">attachment</span><span class="o">();</span>
</span></span><span class="line"><span class="cl">                    <span class="n">SocketChannel</span> <span class="n">sc</span> <span class="o">=</span> <span class="o">(</span><span class="n">SocketChannel</span><span class="o">)</span> <span class="n">key</span><span class="o">.</span><span class="na">channel</span><span class="o">();</span>
</span></span><span class="line"><span class="cl">                    <span class="kt">int</span> <span class="n">write</span> <span class="o">=</span> <span class="n">sc</span><span class="o">.</span><span class="na">write</span><span class="o">(</span><span class="n">buffer</span><span class="o">);</span>
</span></span><span class="line"><span class="cl">                    <span class="n">System</span><span class="o">.</span><span class="na">out</span><span class="o">.</span><span class="na">println</span><span class="o">(</span><span class="s">&#34;实际写入字节:&#34;</span> <span class="o">+</span> <span class="n">write</span><span class="o">);</span>
</span></span><span class="line"><span class="cl">                    <span class="k">if</span> <span class="o">(!</span><span class="n">buffer</span><span class="o">.</span><span class="na">hasRemaining</span><span class="o">())</span> <span class="o">{</span> <span class="c1">// 写完了
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>                        <span class="n">key</span><span class="o">.</span><span class="na">interestOps</span><span class="o">(</span><span class="n">key</span><span class="o">.</span><span class="na">interestOps</span><span class="o">()</span> <span class="o">-</span> <span class="n">SelectionKey</span><span class="o">.</span><span class="na">OP_WRITE</span><span class="o">);</span>
</span></span><span class="line"><span class="cl">                        <span class="n">key</span><span class="o">.</span><span class="na">attach</span><span class="o">(</span><span class="kc">null</span><span class="o">);</span>
</span></span><span class="line"><span class="cl">                    <span class="o">}</span>
</span></span><span class="line"><span class="cl">                <span class="o">}</span>
</span></span><span class="line"><span class="cl">            <span class="o">}</span>
</span></span><span class="line"><span class="cl">        <span class="o">}</span>
</span></span><span class="line"><span class="cl">    <span class="o">}</span>
</span></span><span class="line"><span class="cl"><span class="o">}</span>
</span></span></code></pre></td></tr></table>
</div>
</div><p>客户端</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt"> 1
</span><span class="lnt"> 2
</span><span class="lnt"> 3
</span><span class="lnt"> 4
</span><span class="lnt"> 5
</span><span class="lnt"> 6
</span><span class="lnt"> 7
</span><span class="lnt"> 8
</span><span class="lnt"> 9
</span><span class="lnt">10
</span><span class="lnt">11
</span><span class="lnt">12
</span><span class="lnt">13
</span><span class="lnt">14
</span><span class="lnt">15
</span><span class="lnt">16
</span><span class="lnt">17
</span><span class="lnt">18
</span><span class="lnt">19
</span><span class="lnt">20
</span><span class="lnt">21
</span><span class="lnt">22
</span><span class="lnt">23
</span><span class="lnt">24
</span><span class="lnt">25
</span><span class="lnt">26
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-java" data-lang="java"><span class="line"><span class="cl"><span class="kd">public</span> <span class="kd">class</span> <span class="nc">WriteClient</span> <span class="o">{</span>
</span></span><span class="line"><span class="cl">    <span class="kd">public</span> <span class="kd">static</span> <span class="kt">void</span> <span class="nf">main</span><span class="o">(</span><span class="n">String</span><span class="o">[]</span> <span class="n">args</span><span class="o">)</span> <span class="kd">throws</span> <span class="n">IOException</span> <span class="o">{</span>
</span></span><span class="line"><span class="cl">        <span class="n">Selector</span> <span class="n">selector</span> <span class="o">=</span> <span class="n">Selector</span><span class="o">.</span><span class="na">open</span><span class="o">();</span>
</span></span><span class="line"><span class="cl">        <span class="n">SocketChannel</span> <span class="n">sc</span> <span class="o">=</span> <span class="n">SocketChannel</span><span class="o">.</span><span class="na">open</span><span class="o">();</span>
</span></span><span class="line"><span class="cl">        <span class="n">sc</span><span class="o">.</span><span class="na">configureBlocking</span><span class="o">(</span><span class="kc">false</span><span class="o">);</span>
</span></span><span class="line"><span class="cl">        <span class="n">sc</span><span class="o">.</span><span class="na">register</span><span class="o">(</span><span class="n">selector</span><span class="o">,</span> <span class="n">SelectionKey</span><span class="o">.</span><span class="na">OP_CONNECT</span> <span class="o">|</span> <span class="n">SelectionKey</span><span class="o">.</span><span class="na">OP_READ</span><span class="o">);</span>
</span></span><span class="line"><span class="cl">        <span class="n">sc</span><span class="o">.</span><span class="na">connect</span><span class="o">(</span><span class="k">new</span> <span class="n">InetSocketAddress</span><span class="o">(</span><span class="s">&#34;localhost&#34;</span><span class="o">,</span> <span class="n">8080</span><span class="o">));</span>
</span></span><span class="line"><span class="cl">        <span class="kt">int</span> <span class="n">count</span> <span class="o">=</span> <span class="n">0</span><span class="o">;</span>
</span></span><span class="line"><span class="cl">        <span class="k">while</span> <span class="o">(</span><span class="kc">true</span><span class="o">)</span> <span class="o">{</span>
</span></span><span class="line"><span class="cl">            <span class="n">selector</span><span class="o">.</span><span class="na">select</span><span class="o">();</span>
</span></span><span class="line"><span class="cl">            <span class="n">Iterator</span><span class="o">&lt;</span><span class="n">SelectionKey</span><span class="o">&gt;</span> <span class="n">iter</span> <span class="o">=</span> <span class="n">selector</span><span class="o">.</span><span class="na">selectedKeys</span><span class="o">().</span><span class="na">iterator</span><span class="o">();</span>
</span></span><span class="line"><span class="cl">            <span class="k">while</span> <span class="o">(</span><span class="n">iter</span><span class="o">.</span><span class="na">hasNext</span><span class="o">())</span> <span class="o">{</span>
</span></span><span class="line"><span class="cl">                <span class="n">SelectionKey</span> <span class="n">key</span> <span class="o">=</span> <span class="n">iter</span><span class="o">.</span><span class="na">next</span><span class="o">();</span>
</span></span><span class="line"><span class="cl">                <span class="n">iter</span><span class="o">.</span><span class="na">remove</span><span class="o">();</span>
</span></span><span class="line"><span class="cl">                <span class="k">if</span> <span class="o">(</span><span class="n">key</span><span class="o">.</span><span class="na">isConnectable</span><span class="o">())</span> <span class="o">{</span>
</span></span><span class="line"><span class="cl">                    <span class="n">System</span><span class="o">.</span><span class="na">out</span><span class="o">.</span><span class="na">println</span><span class="o">(</span><span class="n">sc</span><span class="o">.</span><span class="na">finishConnect</span><span class="o">());</span>
</span></span><span class="line"><span class="cl">                <span class="o">}</span> <span class="k">else</span> <span class="k">if</span> <span class="o">(</span><span class="n">key</span><span class="o">.</span><span class="na">isReadable</span><span class="o">())</span> <span class="o">{</span>
</span></span><span class="line"><span class="cl">                    <span class="n">ByteBuffer</span> <span class="n">buffer</span> <span class="o">=</span> <span class="n">ByteBuffer</span><span class="o">.</span><span class="na">allocate</span><span class="o">(</span><span class="n">1024</span> <span class="o">*</span> <span class="n">1024</span><span class="o">);</span>
</span></span><span class="line"><span class="cl">                    <span class="n">count</span> <span class="o">+=</span> <span class="n">sc</span><span class="o">.</span><span class="na">read</span><span class="o">(</span><span class="n">buffer</span><span class="o">);</span>
</span></span><span class="line"><span class="cl">                    <span class="n">buffer</span><span class="o">.</span><span class="na">clear</span><span class="o">();</span>
</span></span><span class="line"><span class="cl">                    <span class="n">System</span><span class="o">.</span><span class="na">out</span><span class="o">.</span><span class="na">println</span><span class="o">(</span><span class="n">count</span><span class="o">);</span>
</span></span><span class="line"><span class="cl">                <span class="o">}</span>
</span></span><span class="line"><span class="cl">            <span class="o">}</span>
</span></span><span class="line"><span class="cl">        <span class="o">}</span>
</span></span><span class="line"><span class="cl">    <span class="o">}</span>
</span></span><span class="line"><span class="cl"><span class="o">}</span>
</span></span></code></pre></td></tr></table>
</div>
</div><h4 id="-write-为何要取消">💡 write 为何要取消</h4>
<p>只要向 channel 发送数据时，socket 缓冲可写，这个事件会频繁触发，因此应当只在 socket 缓冲区写不下时再关注可写事件，数据写完之后再取消关注</p>
<h3 id="46-更进一步">4.6 更进一步</h3>
<h4 id="-利用多线程优化">💡 利用多线程优化</h4>
<blockquote>
<p>现在都是多核 cpu，设计时要充分考虑别让 cpu 的力量被白白浪费</p>
</blockquote>
<p>前面的代码只有一个选择器，没有充分利用多核 cpu，如何改进呢？</p>
<p>分两组选择器</p>
<ul>
<li>单线程配一个选择器，专门处理 accept 事件</li>
<li>创建 cpu 核心数的线程，每个线程配一个选择器，轮流处理 read 事件</li>
</ul>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">  1
</span><span class="lnt">  2
</span><span class="lnt">  3
</span><span class="lnt">  4
</span><span class="lnt">  5
</span><span class="lnt">  6
</span><span class="lnt">  7
</span><span class="lnt">  8
</span><span class="lnt">  9
</span><span class="lnt"> 10
</span><span class="lnt"> 11
</span><span class="lnt"> 12
</span><span class="lnt"> 13
</span><span class="lnt"> 14
</span><span class="lnt"> 15
</span><span class="lnt"> 16
</span><span class="lnt"> 17
</span><span class="lnt"> 18
</span><span class="lnt"> 19
</span><span class="lnt"> 20
</span><span class="lnt"> 21
</span><span class="lnt"> 22
</span><span class="lnt"> 23
</span><span class="lnt"> 24
</span><span class="lnt"> 25
</span><span class="lnt"> 26
</span><span class="lnt"> 27
</span><span class="lnt"> 28
</span><span class="lnt"> 29
</span><span class="lnt"> 30
</span><span class="lnt"> 31
</span><span class="lnt"> 32
</span><span class="lnt"> 33
</span><span class="lnt"> 34
</span><span class="lnt"> 35
</span><span class="lnt"> 36
</span><span class="lnt"> 37
</span><span class="lnt"> 38
</span><span class="lnt"> 39
</span><span class="lnt"> 40
</span><span class="lnt"> 41
</span><span class="lnt"> 42
</span><span class="lnt"> 43
</span><span class="lnt"> 44
</span><span class="lnt"> 45
</span><span class="lnt"> 46
</span><span class="lnt"> 47
</span><span class="lnt"> 48
</span><span class="lnt"> 49
</span><span class="lnt"> 50
</span><span class="lnt"> 51
</span><span class="lnt"> 52
</span><span class="lnt"> 53
</span><span class="lnt"> 54
</span><span class="lnt"> 55
</span><span class="lnt"> 56
</span><span class="lnt"> 57
</span><span class="lnt"> 58
</span><span class="lnt"> 59
</span><span class="lnt"> 60
</span><span class="lnt"> 61
</span><span class="lnt"> 62
</span><span class="lnt"> 63
</span><span class="lnt"> 64
</span><span class="lnt"> 65
</span><span class="lnt"> 66
</span><span class="lnt"> 67
</span><span class="lnt"> 68
</span><span class="lnt"> 69
</span><span class="lnt"> 70
</span><span class="lnt"> 71
</span><span class="lnt"> 72
</span><span class="lnt"> 73
</span><span class="lnt"> 74
</span><span class="lnt"> 75
</span><span class="lnt"> 76
</span><span class="lnt"> 77
</span><span class="lnt"> 78
</span><span class="lnt"> 79
</span><span class="lnt"> 80
</span><span class="lnt"> 81
</span><span class="lnt"> 82
</span><span class="lnt"> 83
</span><span class="lnt"> 84
</span><span class="lnt"> 85
</span><span class="lnt"> 86
</span><span class="lnt"> 87
</span><span class="lnt"> 88
</span><span class="lnt"> 89
</span><span class="lnt"> 90
</span><span class="lnt"> 91
</span><span class="lnt"> 92
</span><span class="lnt"> 93
</span><span class="lnt"> 94
</span><span class="lnt"> 95
</span><span class="lnt"> 96
</span><span class="lnt"> 97
</span><span class="lnt"> 98
</span><span class="lnt"> 99
</span><span class="lnt">100
</span><span class="lnt">101
</span><span class="lnt">102
</span><span class="lnt">103
</span><span class="lnt">104
</span><span class="lnt">105
</span><span class="lnt">106
</span><span class="lnt">107
</span><span class="lnt">108
</span><span class="lnt">109
</span><span class="lnt">110
</span><span class="lnt">111
</span><span class="lnt">112
</span><span class="lnt">113
</span><span class="lnt">114
</span><span class="lnt">115
</span><span class="lnt">116
</span><span class="lnt">117
</span><span class="lnt">118
</span><span class="lnt">119
</span><span class="lnt">120
</span><span class="lnt">121
</span><span class="lnt">122
</span><span class="lnt">123
</span><span class="lnt">124
</span><span class="lnt">125
</span><span class="lnt">126
</span><span class="lnt">127
</span><span class="lnt">128
</span><span class="lnt">129
</span><span class="lnt">130
</span><span class="lnt">131
</span><span class="lnt">132
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-java" data-lang="java"><span class="line"><span class="cl"><span class="kd">public</span> <span class="kd">class</span> <span class="nc">ChannelDemo7</span> <span class="o">{</span>
</span></span><span class="line"><span class="cl">    <span class="kd">public</span> <span class="kd">static</span> <span class="kt">void</span> <span class="nf">main</span><span class="o">(</span><span class="n">String</span><span class="o">[]</span> <span class="n">args</span><span class="o">)</span> <span class="kd">throws</span> <span class="n">IOException</span> <span class="o">{</span>
</span></span><span class="line"><span class="cl">        <span class="k">new</span> <span class="n">BossEventLoop</span><span class="o">().</span><span class="na">register</span><span class="o">();</span>
</span></span><span class="line"><span class="cl">    <span class="o">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="nd">@Slf4j</span>
</span></span><span class="line"><span class="cl">    <span class="kd">static</span> <span class="kd">class</span> <span class="nc">BossEventLoop</span> <span class="kd">implements</span> <span class="n">Runnable</span> <span class="o">{</span>
</span></span><span class="line"><span class="cl">        <span class="kd">private</span> <span class="n">Selector</span> <span class="n">boss</span><span class="o">;</span>
</span></span><span class="line"><span class="cl">        <span class="kd">private</span> <span class="n">WorkerEventLoop</span><span class="o">[]</span> <span class="n">workers</span><span class="o">;</span>
</span></span><span class="line"><span class="cl">        <span class="kd">private</span> <span class="kd">volatile</span> <span class="kt">boolean</span> <span class="n">start</span> <span class="o">=</span> <span class="kc">false</span><span class="o">;</span>
</span></span><span class="line"><span class="cl">        <span class="n">AtomicInteger</span> <span class="n">index</span> <span class="o">=</span> <span class="k">new</span> <span class="n">AtomicInteger</span><span class="o">();</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">        <span class="kd">public</span> <span class="kt">void</span> <span class="nf">register</span><span class="o">()</span> <span class="kd">throws</span> <span class="n">IOException</span> <span class="o">{</span>
</span></span><span class="line"><span class="cl">            <span class="k">if</span> <span class="o">(!</span><span class="n">start</span><span class="o">)</span> <span class="o">{</span>
</span></span><span class="line"><span class="cl">                <span class="n">ServerSocketChannel</span> <span class="n">ssc</span> <span class="o">=</span> <span class="n">ServerSocketChannel</span><span class="o">.</span><span class="na">open</span><span class="o">();</span>
</span></span><span class="line"><span class="cl">                <span class="n">ssc</span><span class="o">.</span><span class="na">bind</span><span class="o">(</span><span class="k">new</span> <span class="n">InetSocketAddress</span><span class="o">(</span><span class="n">8080</span><span class="o">));</span>
</span></span><span class="line"><span class="cl">                <span class="n">ssc</span><span class="o">.</span><span class="na">configureBlocking</span><span class="o">(</span><span class="kc">false</span><span class="o">);</span>
</span></span><span class="line"><span class="cl">                <span class="n">boss</span> <span class="o">=</span> <span class="n">Selector</span><span class="o">.</span><span class="na">open</span><span class="o">();</span>
</span></span><span class="line"><span class="cl">                <span class="n">SelectionKey</span> <span class="n">ssckey</span> <span class="o">=</span> <span class="n">ssc</span><span class="o">.</span><span class="na">register</span><span class="o">(</span><span class="n">boss</span><span class="o">,</span> <span class="n">0</span><span class="o">,</span> <span class="kc">null</span><span class="o">);</span>
</span></span><span class="line"><span class="cl">                <span class="n">ssckey</span><span class="o">.</span><span class="na">interestOps</span><span class="o">(</span><span class="n">SelectionKey</span><span class="o">.</span><span class="na">OP_ACCEPT</span><span class="o">);</span>
</span></span><span class="line"><span class="cl">                <span class="n">workers</span> <span class="o">=</span> <span class="n">initEventLoops</span><span class="o">();</span>
</span></span><span class="line"><span class="cl">                <span class="k">new</span> <span class="n">Thread</span><span class="o">(</span><span class="k">this</span><span class="o">,</span> <span class="s">&#34;boss&#34;</span><span class="o">).</span><span class="na">start</span><span class="o">();</span>
</span></span><span class="line"><span class="cl">                <span class="n">log</span><span class="o">.</span><span class="na">debug</span><span class="o">(</span><span class="s">&#34;boss start...&#34;</span><span class="o">);</span>
</span></span><span class="line"><span class="cl">                <span class="n">start</span> <span class="o">=</span> <span class="kc">true</span><span class="o">;</span>
</span></span><span class="line"><span class="cl">            <span class="o">}</span>
</span></span><span class="line"><span class="cl">        <span class="o">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">        <span class="kd">public</span> <span class="n">WorkerEventLoop</span><span class="o">[]</span> <span class="nf">initEventLoops</span><span class="o">()</span> <span class="o">{</span>
</span></span><span class="line"><span class="cl"><span class="c1">//        EventLoop[] eventLoops = new EventLoop[Runtime.getRuntime().availableProcessors()];
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>            <span class="n">WorkerEventLoop</span><span class="o">[]</span> <span class="n">workerEventLoops</span> <span class="o">=</span> <span class="k">new</span> <span class="n">WorkerEventLoop</span><span class="o">[</span><span class="n">2</span><span class="o">];</span>
</span></span><span class="line"><span class="cl">            <span class="k">for</span> <span class="o">(</span><span class="kt">int</span> <span class="n">i</span> <span class="o">=</span> <span class="n">0</span><span class="o">;</span> <span class="n">i</span> <span class="o">&lt;</span> <span class="n">workerEventLoops</span><span class="o">.</span><span class="na">length</span><span class="o">;</span> <span class="n">i</span><span class="o">++)</span> <span class="o">{</span>
</span></span><span class="line"><span class="cl">                <span class="n">workerEventLoops</span><span class="o">[</span><span class="n">i</span><span class="o">]</span> <span class="o">=</span> <span class="k">new</span> <span class="n">WorkerEventLoop</span><span class="o">(</span><span class="n">i</span><span class="o">);</span>
</span></span><span class="line"><span class="cl">            <span class="o">}</span>
</span></span><span class="line"><span class="cl">            <span class="k">return</span> <span class="n">workerEventLoops</span><span class="o">;</span>
</span></span><span class="line"><span class="cl">        <span class="o">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">        <span class="nd">@Override</span>
</span></span><span class="line"><span class="cl">        <span class="kd">public</span> <span class="kt">void</span> <span class="nf">run</span><span class="o">()</span> <span class="o">{</span>
</span></span><span class="line"><span class="cl">            <span class="k">while</span> <span class="o">(</span><span class="kc">true</span><span class="o">)</span> <span class="o">{</span>
</span></span><span class="line"><span class="cl">                <span class="k">try</span> <span class="o">{</span>
</span></span><span class="line"><span class="cl">                    <span class="n">boss</span><span class="o">.</span><span class="na">select</span><span class="o">();</span>
</span></span><span class="line"><span class="cl">                    <span class="n">Iterator</span><span class="o">&lt;</span><span class="n">SelectionKey</span><span class="o">&gt;</span> <span class="n">iter</span> <span class="o">=</span> <span class="n">boss</span><span class="o">.</span><span class="na">selectedKeys</span><span class="o">().</span><span class="na">iterator</span><span class="o">();</span>
</span></span><span class="line"><span class="cl">                    <span class="k">while</span> <span class="o">(</span><span class="n">iter</span><span class="o">.</span><span class="na">hasNext</span><span class="o">())</span> <span class="o">{</span>
</span></span><span class="line"><span class="cl">                        <span class="n">SelectionKey</span> <span class="n">key</span> <span class="o">=</span> <span class="n">iter</span><span class="o">.</span><span class="na">next</span><span class="o">();</span>
</span></span><span class="line"><span class="cl">                        <span class="n">iter</span><span class="o">.</span><span class="na">remove</span><span class="o">();</span>
</span></span><span class="line"><span class="cl">                        <span class="k">if</span> <span class="o">(</span><span class="n">key</span><span class="o">.</span><span class="na">isAcceptable</span><span class="o">())</span> <span class="o">{</span>
</span></span><span class="line"><span class="cl">                            <span class="n">ServerSocketChannel</span> <span class="n">c</span> <span class="o">=</span> <span class="o">(</span><span class="n">ServerSocketChannel</span><span class="o">)</span> <span class="n">key</span><span class="o">.</span><span class="na">channel</span><span class="o">();</span>
</span></span><span class="line"><span class="cl">                            <span class="n">SocketChannel</span> <span class="n">sc</span> <span class="o">=</span> <span class="n">c</span><span class="o">.</span><span class="na">accept</span><span class="o">();</span>
</span></span><span class="line"><span class="cl">                            <span class="n">sc</span><span class="o">.</span><span class="na">configureBlocking</span><span class="o">(</span><span class="kc">false</span><span class="o">);</span>
</span></span><span class="line"><span class="cl">                            <span class="n">log</span><span class="o">.</span><span class="na">debug</span><span class="o">(</span><span class="s">&#34;{} connected&#34;</span><span class="o">,</span> <span class="n">sc</span><span class="o">.</span><span class="na">getRemoteAddress</span><span class="o">());</span>
</span></span><span class="line"><span class="cl">                            <span class="n">workers</span><span class="o">[</span><span class="n">index</span><span class="o">.</span><span class="na">getAndIncrement</span><span class="o">()</span> <span class="o">%</span> <span class="n">workers</span><span class="o">.</span><span class="na">length</span><span class="o">].</span><span class="na">register</span><span class="o">(</span><span class="n">sc</span><span class="o">);</span>
</span></span><span class="line"><span class="cl">                        <span class="o">}</span>
</span></span><span class="line"><span class="cl">                    <span class="o">}</span>
</span></span><span class="line"><span class="cl">                <span class="o">}</span> <span class="k">catch</span> <span class="o">(</span><span class="n">IOException</span> <span class="n">e</span><span class="o">)</span> <span class="o">{</span>
</span></span><span class="line"><span class="cl">                    <span class="n">e</span><span class="o">.</span><span class="na">printStackTrace</span><span class="o">();</span>
</span></span><span class="line"><span class="cl">                <span class="o">}</span>
</span></span><span class="line"><span class="cl">            <span class="o">}</span>
</span></span><span class="line"><span class="cl">        <span class="o">}</span>
</span></span><span class="line"><span class="cl">    <span class="o">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="nd">@Slf4j</span>
</span></span><span class="line"><span class="cl">    <span class="kd">static</span> <span class="kd">class</span> <span class="nc">WorkerEventLoop</span> <span class="kd">implements</span> <span class="n">Runnable</span> <span class="o">{</span>
</span></span><span class="line"><span class="cl">        <span class="kd">private</span> <span class="n">Selector</span> <span class="n">worker</span><span class="o">;</span>
</span></span><span class="line"><span class="cl">        <span class="kd">private</span> <span class="kd">volatile</span> <span class="kt">boolean</span> <span class="n">start</span> <span class="o">=</span> <span class="kc">false</span><span class="o">;</span>
</span></span><span class="line"><span class="cl">        <span class="kd">private</span> <span class="kt">int</span> <span class="n">index</span><span class="o">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">        <span class="kd">private</span> <span class="kd">final</span> <span class="n">ConcurrentLinkedQueue</span><span class="o">&lt;</span><span class="n">Runnable</span><span class="o">&gt;</span> <span class="n">tasks</span> <span class="o">=</span> <span class="k">new</span> <span class="n">ConcurrentLinkedQueue</span><span class="o">&lt;&gt;();</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">        <span class="kd">public</span> <span class="nf">WorkerEventLoop</span><span class="o">(</span><span class="kt">int</span> <span class="n">index</span><span class="o">)</span> <span class="o">{</span>
</span></span><span class="line"><span class="cl">            <span class="k">this</span><span class="o">.</span><span class="na">index</span> <span class="o">=</span> <span class="n">index</span><span class="o">;</span>
</span></span><span class="line"><span class="cl">        <span class="o">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">        <span class="kd">public</span> <span class="kt">void</span> <span class="nf">register</span><span class="o">(</span><span class="n">SocketChannel</span> <span class="n">sc</span><span class="o">)</span> <span class="kd">throws</span> <span class="n">IOException</span> <span class="o">{</span>
</span></span><span class="line"><span class="cl">            <span class="k">if</span> <span class="o">(!</span><span class="n">start</span><span class="o">)</span> <span class="o">{</span>
</span></span><span class="line"><span class="cl">                <span class="n">worker</span> <span class="o">=</span> <span class="n">Selector</span><span class="o">.</span><span class="na">open</span><span class="o">();</span>
</span></span><span class="line"><span class="cl">                <span class="k">new</span> <span class="n">Thread</span><span class="o">(</span><span class="k">this</span><span class="o">,</span> <span class="s">&#34;worker-&#34;</span> <span class="o">+</span> <span class="n">index</span><span class="o">).</span><span class="na">start</span><span class="o">();</span>
</span></span><span class="line"><span class="cl">                <span class="n">start</span> <span class="o">=</span> <span class="kc">true</span><span class="o">;</span>
</span></span><span class="line"><span class="cl">            <span class="o">}</span>
</span></span><span class="line"><span class="cl">            <span class="n">tasks</span><span class="o">.</span><span class="na">add</span><span class="o">(()</span> <span class="o">-&gt;</span> <span class="o">{</span>
</span></span><span class="line"><span class="cl">                <span class="k">try</span> <span class="o">{</span>
</span></span><span class="line"><span class="cl">                    <span class="n">SelectionKey</span> <span class="n">sckey</span> <span class="o">=</span> <span class="n">sc</span><span class="o">.</span><span class="na">register</span><span class="o">(</span><span class="n">worker</span><span class="o">,</span> <span class="n">0</span><span class="o">,</span> <span class="kc">null</span><span class="o">);</span>
</span></span><span class="line"><span class="cl">                    <span class="n">sckey</span><span class="o">.</span><span class="na">interestOps</span><span class="o">(</span><span class="n">SelectionKey</span><span class="o">.</span><span class="na">OP_READ</span><span class="o">);</span>
</span></span><span class="line"><span class="cl">                    <span class="n">worker</span><span class="o">.</span><span class="na">selectNow</span><span class="o">();</span>
</span></span><span class="line"><span class="cl">                <span class="o">}</span> <span class="k">catch</span> <span class="o">(</span><span class="n">IOException</span> <span class="n">e</span><span class="o">)</span> <span class="o">{</span>
</span></span><span class="line"><span class="cl">                    <span class="n">e</span><span class="o">.</span><span class="na">printStackTrace</span><span class="o">();</span>
</span></span><span class="line"><span class="cl">                <span class="o">}</span>
</span></span><span class="line"><span class="cl">            <span class="o">});</span>
</span></span><span class="line"><span class="cl">            <span class="n">worker</span><span class="o">.</span><span class="na">wakeup</span><span class="o">();</span>
</span></span><span class="line"><span class="cl">        <span class="o">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">        <span class="nd">@Override</span>
</span></span><span class="line"><span class="cl">        <span class="kd">public</span> <span class="kt">void</span> <span class="nf">run</span><span class="o">()</span> <span class="o">{</span>
</span></span><span class="line"><span class="cl">            <span class="k">while</span> <span class="o">(</span><span class="kc">true</span><span class="o">)</span> <span class="o">{</span>
</span></span><span class="line"><span class="cl">                <span class="k">try</span> <span class="o">{</span>
</span></span><span class="line"><span class="cl">                    <span class="n">worker</span><span class="o">.</span><span class="na">select</span><span class="o">();</span>
</span></span><span class="line"><span class="cl">                    <span class="n">Runnable</span> <span class="n">task</span> <span class="o">=</span> <span class="n">tasks</span><span class="o">.</span><span class="na">poll</span><span class="o">();</span>
</span></span><span class="line"><span class="cl">                    <span class="k">if</span> <span class="o">(</span><span class="n">task</span> <span class="o">!=</span> <span class="kc">null</span><span class="o">)</span> <span class="o">{</span>
</span></span><span class="line"><span class="cl">                        <span class="n">task</span><span class="o">.</span><span class="na">run</span><span class="o">();</span>
</span></span><span class="line"><span class="cl">                    <span class="o">}</span>
</span></span><span class="line"><span class="cl">                    <span class="n">Set</span><span class="o">&lt;</span><span class="n">SelectionKey</span><span class="o">&gt;</span> <span class="n">keys</span> <span class="o">=</span> <span class="n">worker</span><span class="o">.</span><span class="na">selectedKeys</span><span class="o">();</span>
</span></span><span class="line"><span class="cl">                    <span class="n">Iterator</span><span class="o">&lt;</span><span class="n">SelectionKey</span><span class="o">&gt;</span> <span class="n">iter</span> <span class="o">=</span> <span class="n">keys</span><span class="o">.</span><span class="na">iterator</span><span class="o">();</span>
</span></span><span class="line"><span class="cl">                    <span class="k">while</span> <span class="o">(</span><span class="n">iter</span><span class="o">.</span><span class="na">hasNext</span><span class="o">())</span> <span class="o">{</span>
</span></span><span class="line"><span class="cl">                        <span class="n">SelectionKey</span> <span class="n">key</span> <span class="o">=</span> <span class="n">iter</span><span class="o">.</span><span class="na">next</span><span class="o">();</span>
</span></span><span class="line"><span class="cl">                        <span class="k">if</span> <span class="o">(</span><span class="n">key</span><span class="o">.</span><span class="na">isReadable</span><span class="o">())</span> <span class="o">{</span>
</span></span><span class="line"><span class="cl">                            <span class="n">SocketChannel</span> <span class="n">sc</span> <span class="o">=</span> <span class="o">(</span><span class="n">SocketChannel</span><span class="o">)</span> <span class="n">key</span><span class="o">.</span><span class="na">channel</span><span class="o">();</span>
</span></span><span class="line"><span class="cl">                            <span class="n">ByteBuffer</span> <span class="n">buffer</span> <span class="o">=</span> <span class="n">ByteBuffer</span><span class="o">.</span><span class="na">allocate</span><span class="o">(</span><span class="n">128</span><span class="o">);</span>
</span></span><span class="line"><span class="cl">                            <span class="k">try</span> <span class="o">{</span>
</span></span><span class="line"><span class="cl">                                <span class="kt">int</span> <span class="n">read</span> <span class="o">=</span> <span class="n">sc</span><span class="o">.</span><span class="na">read</span><span class="o">(</span><span class="n">buffer</span><span class="o">);</span>
</span></span><span class="line"><span class="cl">                                <span class="k">if</span> <span class="o">(</span><span class="n">read</span> <span class="o">==</span> <span class="o">-</span><span class="n">1</span><span class="o">)</span> <span class="o">{</span>
</span></span><span class="line"><span class="cl">                                    <span class="n">key</span><span class="o">.</span><span class="na">cancel</span><span class="o">();</span>
</span></span><span class="line"><span class="cl">                                    <span class="n">sc</span><span class="o">.</span><span class="na">close</span><span class="o">();</span>
</span></span><span class="line"><span class="cl">                                <span class="o">}</span> <span class="k">else</span> <span class="o">{</span>
</span></span><span class="line"><span class="cl">                                    <span class="n">buffer</span><span class="o">.</span><span class="na">flip</span><span class="o">();</span>
</span></span><span class="line"><span class="cl">                                    <span class="n">log</span><span class="o">.</span><span class="na">debug</span><span class="o">(</span><span class="s">&#34;{} message:&#34;</span><span class="o">,</span> <span class="n">sc</span><span class="o">.</span><span class="na">getRemoteAddress</span><span class="o">());</span>
</span></span><span class="line"><span class="cl">                                    <span class="n">debugAll</span><span class="o">(</span><span class="n">buffer</span><span class="o">);</span>
</span></span><span class="line"><span class="cl">                                <span class="o">}</span>
</span></span><span class="line"><span class="cl">                            <span class="o">}</span> <span class="k">catch</span> <span class="o">(</span><span class="n">IOException</span> <span class="n">e</span><span class="o">)</span> <span class="o">{</span>
</span></span><span class="line"><span class="cl">                                <span class="n">e</span><span class="o">.</span><span class="na">printStackTrace</span><span class="o">();</span>
</span></span><span class="line"><span class="cl">                                <span class="n">key</span><span class="o">.</span><span class="na">cancel</span><span class="o">();</span>
</span></span><span class="line"><span class="cl">                                <span class="n">sc</span><span class="o">.</span><span class="na">close</span><span class="o">();</span>
</span></span><span class="line"><span class="cl">                            <span class="o">}</span>
</span></span><span class="line"><span class="cl">                        <span class="o">}</span>
</span></span><span class="line"><span class="cl">                        <span class="n">iter</span><span class="o">.</span><span class="na">remove</span><span class="o">();</span>
</span></span><span class="line"><span class="cl">                    <span class="o">}</span>
</span></span><span class="line"><span class="cl">                <span class="o">}</span> <span class="k">catch</span> <span class="o">(</span><span class="n">IOException</span> <span class="n">e</span><span class="o">)</span> <span class="o">{</span>
</span></span><span class="line"><span class="cl">                    <span class="n">e</span><span class="o">.</span><span class="na">printStackTrace</span><span class="o">();</span>
</span></span><span class="line"><span class="cl">                <span class="o">}</span>
</span></span><span class="line"><span class="cl">            <span class="o">}</span>
</span></span><span class="line"><span class="cl">        <span class="o">}</span>
</span></span><span class="line"><span class="cl">    <span class="o">}</span>
</span></span><span class="line"><span class="cl"><span class="o">}</span>
</span></span></code></pre></td></tr></table>
</div>
</div><h4 id="-如何拿到-cpu-个数">💡 如何拿到 cpu 个数</h4>
<blockquote>
<ul>
<li>Runtime.getRuntime().availableProcessors() 如果工作在 docker 容器下，因为容器不是物理隔离的，会拿到物理 cpu 个数，而不是容器申请时的个数</li>
<li>这个问题直到 jdk 10 才修复，使用 jvm 参数 UseContainerSupport 配置， 默认开启</li>
</ul>
</blockquote>
<h3 id="47-udp">4.7 UDP</h3>
<ul>
<li>UDP 是无连接的，client 发送数据不会管 server 是否开启</li>
<li>server 这边的 receive 方法会将接收到的数据存入 byte buffer，但如果数据报文超过 buffer 大小，多出来的数据会被默默抛弃</li>
</ul>
<p>首先启动服务器端</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt"> 1
</span><span class="lnt"> 2
</span><span class="lnt"> 3
</span><span class="lnt"> 4
</span><span class="lnt"> 5
</span><span class="lnt"> 6
</span><span class="lnt"> 7
</span><span class="lnt"> 8
</span><span class="lnt"> 9
</span><span class="lnt">10
</span><span class="lnt">11
</span><span class="lnt">12
</span><span class="lnt">13
</span><span class="lnt">14
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-java" data-lang="java"><span class="line"><span class="cl"><span class="kd">public</span> <span class="kd">class</span> <span class="nc">UdpServer</span> <span class="o">{</span>
</span></span><span class="line"><span class="cl">    <span class="kd">public</span> <span class="kd">static</span> <span class="kt">void</span> <span class="nf">main</span><span class="o">(</span><span class="n">String</span><span class="o">[]</span> <span class="n">args</span><span class="o">)</span> <span class="o">{</span>
</span></span><span class="line"><span class="cl">        <span class="k">try</span> <span class="o">(</span><span class="n">DatagramChannel</span> <span class="n">channel</span> <span class="o">=</span> <span class="n">DatagramChannel</span><span class="o">.</span><span class="na">open</span><span class="o">())</span> <span class="o">{</span>
</span></span><span class="line"><span class="cl">            <span class="n">channel</span><span class="o">.</span><span class="na">socket</span><span class="o">().</span><span class="na">bind</span><span class="o">(</span><span class="k">new</span> <span class="n">InetSocketAddress</span><span class="o">(</span><span class="n">9999</span><span class="o">));</span>
</span></span><span class="line"><span class="cl">            <span class="n">System</span><span class="o">.</span><span class="na">out</span><span class="o">.</span><span class="na">println</span><span class="o">(</span><span class="s">&#34;waiting...&#34;</span><span class="o">);</span>
</span></span><span class="line"><span class="cl">            <span class="n">ByteBuffer</span> <span class="n">buffer</span> <span class="o">=</span> <span class="n">ByteBuffer</span><span class="o">.</span><span class="na">allocate</span><span class="o">(</span><span class="n">32</span><span class="o">);</span>
</span></span><span class="line"><span class="cl">            <span class="n">channel</span><span class="o">.</span><span class="na">receive</span><span class="o">(</span><span class="n">buffer</span><span class="o">);</span>
</span></span><span class="line"><span class="cl">            <span class="n">buffer</span><span class="o">.</span><span class="na">flip</span><span class="o">();</span>
</span></span><span class="line"><span class="cl">            <span class="n">debug</span><span class="o">(</span><span class="n">buffer</span><span class="o">);</span>
</span></span><span class="line"><span class="cl">        <span class="o">}</span> <span class="k">catch</span> <span class="o">(</span><span class="n">IOException</span> <span class="n">e</span><span class="o">)</span> <span class="o">{</span>
</span></span><span class="line"><span class="cl">            <span class="n">e</span><span class="o">.</span><span class="na">printStackTrace</span><span class="o">();</span>
</span></span><span class="line"><span class="cl">        <span class="o">}</span>
</span></span><span class="line"><span class="cl">    <span class="o">}</span>
</span></span><span class="line"><span class="cl"><span class="o">}</span>
</span></span></code></pre></td></tr></table>
</div>
</div><p>输出</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-fallback" data-lang="fallback"><span class="line"><span class="cl">waiting...
</span></span></code></pre></td></tr></table>
</div>
</div><p>运行客户端</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt"> 1
</span><span class="lnt"> 2
</span><span class="lnt"> 3
</span><span class="lnt"> 4
</span><span class="lnt"> 5
</span><span class="lnt"> 6
</span><span class="lnt"> 7
</span><span class="lnt"> 8
</span><span class="lnt"> 9
</span><span class="lnt">10
</span><span class="lnt">11
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-java" data-lang="java"><span class="line"><span class="cl"><span class="kd">public</span> <span class="kd">class</span> <span class="nc">UdpClient</span> <span class="o">{</span>
</span></span><span class="line"><span class="cl">    <span class="kd">public</span> <span class="kd">static</span> <span class="kt">void</span> <span class="nf">main</span><span class="o">(</span><span class="n">String</span><span class="o">[]</span> <span class="n">args</span><span class="o">)</span> <span class="o">{</span>
</span></span><span class="line"><span class="cl">        <span class="k">try</span> <span class="o">(</span><span class="n">DatagramChannel</span> <span class="n">channel</span> <span class="o">=</span> <span class="n">DatagramChannel</span><span class="o">.</span><span class="na">open</span><span class="o">())</span> <span class="o">{</span>
</span></span><span class="line"><span class="cl">            <span class="n">ByteBuffer</span> <span class="n">buffer</span> <span class="o">=</span> <span class="n">StandardCharsets</span><span class="o">.</span><span class="na">UTF_8</span><span class="o">.</span><span class="na">encode</span><span class="o">(</span><span class="s">&#34;hello&#34;</span><span class="o">);</span>
</span></span><span class="line"><span class="cl">            <span class="n">InetSocketAddress</span> <span class="n">address</span> <span class="o">=</span> <span class="k">new</span> <span class="n">InetSocketAddress</span><span class="o">(</span><span class="s">&#34;localhost&#34;</span><span class="o">,</span> <span class="n">9999</span><span class="o">);</span>
</span></span><span class="line"><span class="cl">            <span class="n">channel</span><span class="o">.</span><span class="na">send</span><span class="o">(</span><span class="n">buffer</span><span class="o">,</span> <span class="n">address</span><span class="o">);</span>
</span></span><span class="line"><span class="cl">        <span class="o">}</span> <span class="k">catch</span> <span class="o">(</span><span class="n">Exception</span> <span class="n">e</span><span class="o">)</span> <span class="o">{</span>
</span></span><span class="line"><span class="cl">            <span class="n">e</span><span class="o">.</span><span class="na">printStackTrace</span><span class="o">();</span>
</span></span><span class="line"><span class="cl">        <span class="o">}</span>
</span></span><span class="line"><span class="cl">    <span class="o">}</span>
</span></span><span class="line"><span class="cl"><span class="o">}</span>
</span></span></code></pre></td></tr></table>
</div>
</div><p>接下来服务器端输出</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span><span class="lnt">2
</span><span class="lnt">3
</span><span class="lnt">4
</span><span class="lnt">5
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-fallback" data-lang="fallback"><span class="line"><span class="cl">         +-------------------------------------------------+
</span></span><span class="line"><span class="cl">         |  0  1  2  3  4  5  6  7  8  9  a  b  c  d  e  f |
</span></span><span class="line"><span class="cl">+--------+-------------------------------------------------+----------------+
</span></span><span class="line"><span class="cl">|00000000| 68 65 6c 6c 6f                                  |hello           |
</span></span><span class="line"><span class="cl">+--------+-------------------------------------------------+----------------+
</span></span></code></pre></td></tr></table>
</div>
</div><h2 id="5-nio-vs-bio">5. NIO vs BIO</h2>
<h3 id="51-stream-vs-channel">5.1 stream vs channel</h3>
<ul>
<li>stream 不会自动缓冲数据，channel 会利用系统提供的发送缓冲区、接收缓冲区（更为底层）</li>
<li>stream 仅支持阻塞 API，channel 同时支持阻塞、非阻塞 API，网络 channel 可配合 selector 实现多路复用</li>
<li>二者均为全双工，即读写可以同时进行</li>
</ul>
<h3 id="52-io-模型">5.2 IO 模型</h3>
<p>同步阻塞、同步非阻塞、同步多路复用、异步阻塞（没有此情况）、异步非阻塞</p>
<ul>
<li>同步：线程自己去获取结果（一个线程）</li>
<li>异步：线程自己不去获取结果，而是由其它线程送结果（至少两个线程）</li>
</ul>
<p>当调用一次 channel.read 或 stream.read 后，会切换至操作系统内核态来完成真正数据读取，而读取又分为两个阶段，分别为：</p>
<ul>
<li>等待数据阶段</li>
<li>复制数据阶段</li>
</ul>
<p><img
        class="lazyload"
        src="/svg/loading.min.svg"
        data-src="/img/0033.png"
        data-srcset="/img/0033.png, /img/0033.png 1.5x, /img/0033.png 2x"
        data-sizes="auto"
        alt="/img/0033.png"
        title="/img/0033.png" /></p>
<ul>
<li>
<p>阻塞 IO</p>
<p><img
        class="lazyload"
        src="/svg/loading.min.svg"
        data-src="/img/0039.png"
        data-srcset="/img/0039.png, /img/0039.png 1.5x, /img/0039.png 2x"
        data-sizes="auto"
        alt="/img/0039.png"
        title="/img/0039.png" /></p>
</li>
<li>
<p>非阻塞  IO</p>
<p><img
        class="lazyload"
        src="/svg/loading.min.svg"
        data-src="/img/0035.png"
        data-srcset="/img/0035.png, /img/0035.png 1.5x, /img/0035.png 2x"
        data-sizes="auto"
        alt="/img/0035.png"
        title="/img/0035.png" /></p>
</li>
<li>
<p>多路复用</p>
<p><img
        class="lazyload"
        src="/svg/loading.min.svg"
        data-src="/img/0038.png"
        data-srcset="/img/0038.png, /img/0038.png 1.5x, /img/0038.png 2x"
        data-sizes="auto"
        alt="/img/0038.png"
        title="/img/0038.png" /></p>
</li>
<li>
<p>信号驱动</p>
</li>
<li>
<p>异步 IO</p>
<p><img
        class="lazyload"
        src="/svg/loading.min.svg"
        data-src="/img/0037.png"
        data-srcset="/img/0037.png, /img/0037.png 1.5x, /img/0037.png 2x"
        data-sizes="auto"
        alt="/img/0037.png"
        title="/img/0037.png" /></p>
</li>
<li>
<p>阻塞 IO vs 多路复用</p>
<p><img
        class="lazyload"
        src="/svg/loading.min.svg"
        data-src="/img/0034.png"
        data-srcset="/img/0034.png, /img/0034.png 1.5x, /img/0034.png 2x"
        data-sizes="auto"
        alt="/img/0034.png"
        title="/img/0034.png" /></p>
<p><img
        class="lazyload"
        src="/svg/loading.min.svg"
        data-src="/img/0036.png"
        data-srcset="/img/0036.png, /img/0036.png 1.5x, /img/0036.png 2x"
        data-sizes="auto"
        alt="/img/0036.png"
        title="/img/0036.png" /></p>
</li>
</ul>
<h4 id="-参考">🔖 参考</h4>
<p>UNIX 网络编程 - 卷 I</p>
<h3 id="53-零拷贝">5.3 零拷贝</h3>
<h4 id="传统-io-问题">传统 IO 问题</h4>
<p>传统的 IO 将一个文件通过 socket 写出</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span><span class="lnt">2
</span><span class="lnt">3
</span><span class="lnt">4
</span><span class="lnt">5
</span><span class="lnt">6
</span><span class="lnt">7
</span><span class="lnt">8
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-java" data-lang="java"><span class="line"><span class="cl"><span class="n">File</span> <span class="n">f</span> <span class="o">=</span> <span class="k">new</span> <span class="n">File</span><span class="o">(</span><span class="s">&#34;helloword/data.txt&#34;</span><span class="o">);</span>
</span></span><span class="line"><span class="cl"><span class="n">RandomAccessFile</span> <span class="n">file</span> <span class="o">=</span> <span class="k">new</span> <span class="n">RandomAccessFile</span><span class="o">(</span><span class="n">file</span><span class="o">,</span> <span class="s">&#34;r&#34;</span><span class="o">);</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kt">byte</span><span class="o">[]</span> <span class="n">buf</span> <span class="o">=</span> <span class="k">new</span> <span class="kt">byte</span><span class="o">[(</span><span class="kt">int</span><span class="o">)</span><span class="n">f</span><span class="o">.</span><span class="na">length</span><span class="o">()];</span>
</span></span><span class="line"><span class="cl"><span class="n">file</span><span class="o">.</span><span class="na">read</span><span class="o">(</span><span class="n">buf</span><span class="o">);</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="n">Socket</span> <span class="n">socket</span> <span class="o">=</span> <span class="o">...;</span>
</span></span><span class="line"><span class="cl"><span class="n">socket</span><span class="o">.</span><span class="na">getOutputStream</span><span class="o">().</span><span class="na">write</span><span class="o">(</span><span class="n">buf</span><span class="o">);</span>
</span></span></code></pre></td></tr></table>
</div>
</div><p>内部工作流程是这样的：</p>
<p><img
        class="lazyload"
        src="/svg/loading.min.svg"
        data-src="/img/0024.png"
        data-srcset="/img/0024.png, /img/0024.png 1.5x, /img/0024.png 2x"
        data-sizes="auto"
        alt="/img/0024.png"
        title="/img/0024.png" /></p>
<ol>
<li>
<p>java 本身并不具备 IO 读写能力，因此 read 方法调用后，要从 java 程序的<strong>用户态</strong>切换至<strong>内核态</strong>，去调用操作系统（Kernel）的读能力，将数据读入<strong>内核缓冲区</strong>。这期间用户线程阻塞，操作系统使用 DMA（Direct Memory Access）来实现文件读，其间也不会使用 cpu</p>
<blockquote>
<p>DMA 也可以理解为硬件单元，用来解放 cpu 完成文件 IO</p>
</blockquote>
</li>
<li>
<p>从<strong>内核态</strong>切换回<strong>用户态</strong>，将数据从<strong>内核缓冲区</strong>读入<strong>用户缓冲区</strong>（即 byte[] buf），这期间 cpu 会参与拷贝，无法利用 DMA</p>
</li>
<li>
<p>调用 write 方法，这时将数据从<strong>用户缓冲区</strong>（byte[] buf）写入 <strong>socket 缓冲区</strong>，cpu 会参与拷贝</p>
</li>
<li>
<p>接下来要向网卡写数据，这项能力 java 又不具备，因此又得从<strong>用户态</strong>切换至<strong>内核态</strong>，调用操作系统的写能力，使用 DMA 将 <strong>socket 缓冲区</strong>的数据写入网卡，不会使用 cpu</p>
</li>
</ol>
<p>可以看到中间环节较多，java 的 IO 实际不是物理设备级别的读写，而是缓存的复制，底层的真正读写是操作系统来完成的</p>
<ul>
<li>用户态与内核态的切换发生了 3 次，这个操作比较重量级</li>
<li>数据拷贝了共 4 次</li>
</ul>
<h4 id="nio-优化">NIO 优化</h4>
<p>通过 DirectByteBuf</p>
<ul>
<li>ByteBuffer.allocate(10)  HeapByteBuffer 使用的还是 java 内存</li>
<li>ByteBuffer.allocateDirect(10)  DirectByteBuffer 使用的是操作系统内存</li>
</ul>
<p><img
        class="lazyload"
        src="/svg/loading.min.svg"
        data-src="/img/0025.png"
        data-srcset="/img/0025.png, /img/0025.png 1.5x, /img/0025.png 2x"
        data-sizes="auto"
        alt="/img/0025.png"
        title="/img/0025.png" /></p>
<p>大部分步骤与优化前相同，不再赘述。唯有一点：java 可以使用 DirectByteBuf 将堆外内存映射到 jvm 内存中来直接访问使用</p>
<ul>
<li>这块内存不受 jvm 垃圾回收的影响，因此内存地址固定，有助于 IO 读写</li>
<li>java 中的 DirectByteBuf 对象仅维护了此内存的虚引用，内存回收分成两步
<ul>
<li>DirectByteBuf 对象被垃圾回收，将虚引用加入引用队列</li>
<li>通过专门线程访问引用队列，根据虚引用释放堆外内存</li>
</ul>
</li>
<li>减少了一次数据拷贝，用户态与内核态的切换次数没有减少</li>
</ul>
<p>进一步优化（底层采用了 linux 2.1 后提供的 sendFile 方法），java 中对应着两个 channel 调用 transferTo/transferFrom 方法拷贝数据</p>
<p><img
        class="lazyload"
        src="/svg/loading.min.svg"
        data-src="/img/0026.png"
        data-srcset="/img/0026.png, /img/0026.png 1.5x, /img/0026.png 2x"
        data-sizes="auto"
        alt="/img/0026.png"
        title="/img/0026.png" /></p>
<ol>
<li>java 调用 transferTo 方法后，要从 java 程序的<strong>用户态</strong>切换至<strong>内核态</strong>，使用 DMA将数据读入<strong>内核缓冲区</strong>，不会使用 cpu</li>
<li>数据从<strong>内核缓冲区</strong>传输到 <strong>socket 缓冲区</strong>，cpu 会参与拷贝</li>
<li>最后使用 DMA 将 <strong>socket 缓冲区</strong>的数据写入网卡，不会使用 cpu</li>
</ol>
<p>可以看到</p>
<ul>
<li>只发生了一次用户态与内核态的切换</li>
<li>数据拷贝了 3 次</li>
</ul>
<p>进一步优化（linux 2.4）</p>
<p><img
        class="lazyload"
        src="/svg/loading.min.svg"
        data-src="/img/0027.png"
        data-srcset="/img/0027.png, /img/0027.png 1.5x, /img/0027.png 2x"
        data-sizes="auto"
        alt="/img/0027.png"
        title="/img/0027.png" /></p>
<ol>
<li>java 调用 transferTo 方法后，要从 java 程序的<strong>用户态</strong>切换至<strong>内核态</strong>，使用 DMA将数据读入<strong>内核缓冲区</strong>，不会使用 cpu</li>
<li>只会将一些 offset 和 length 信息拷入 <strong>socket 缓冲区</strong>，几乎无消耗</li>
<li>使用 DMA 将 <strong>内核缓冲区</strong>的数据写入网卡，不会使用 cpu</li>
</ol>
<p>整个过程仅只发生了一次用户态与内核态的切换，数据拷贝了 2 次。所谓的【零拷贝】，并不是真正无拷贝，而是在不会拷贝重复数据到 jvm 内存中，零拷贝的优点有</p>
<ul>
<li>更少的用户态与内核态的切换</li>
<li>不利用 cpu 计算，减少 cpu 缓存伪共享</li>
<li>零拷贝适合小文件传输</li>
</ul>
<h3 id="53-aio">5.3 AIO</h3>
<p>AIO 用来解决数据复制阶段的阻塞问题</p>
<ul>
<li>同步意味着，在进行读写操作时，线程需要等待结果，还是相当于闲置</li>
<li>异步意味着，在进行读写操作时，线程不必等待结果，而是将来由操作系统来通过回调方式由另外的线程来获得结果</li>
</ul>
<blockquote>
<p>异步模型需要底层操作系统（Kernel）提供支持</p>
<ul>
<li>Windows 系统通过 IOCP 实现了真正的异步 IO</li>
<li>Linux 系统异步 IO 在 2.6 版本引入，但其底层实现还是用多路复用模拟了异步 IO，性能没有优势</li>
</ul>
</blockquote>
<h4 id="文件-aio">文件 AIO</h4>
<p>先来看看 AsynchronousFileChannel</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt"> 1
</span><span class="lnt"> 2
</span><span class="lnt"> 3
</span><span class="lnt"> 4
</span><span class="lnt"> 5
</span><span class="lnt"> 6
</span><span class="lnt"> 7
</span><span class="lnt"> 8
</span><span class="lnt"> 9
</span><span class="lnt">10
</span><span class="lnt">11
</span><span class="lnt">12
</span><span class="lnt">13
</span><span class="lnt">14
</span><span class="lnt">15
</span><span class="lnt">16
</span><span class="lnt">17
</span><span class="lnt">18
</span><span class="lnt">19
</span><span class="lnt">20
</span><span class="lnt">21
</span><span class="lnt">22
</span><span class="lnt">23
</span><span class="lnt">24
</span><span class="lnt">25
</span><span class="lnt">26
</span><span class="lnt">27
</span><span class="lnt">28
</span><span class="lnt">29
</span><span class="lnt">30
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-java" data-lang="java"><span class="line"><span class="cl"><span class="nd">@Slf4j</span>
</span></span><span class="line"><span class="cl"><span class="kd">public</span> <span class="kd">class</span> <span class="nc">AioDemo1</span> <span class="o">{</span>
</span></span><span class="line"><span class="cl">    <span class="kd">public</span> <span class="kd">static</span> <span class="kt">void</span> <span class="nf">main</span><span class="o">(</span><span class="n">String</span><span class="o">[]</span> <span class="n">args</span><span class="o">)</span> <span class="kd">throws</span> <span class="n">IOException</span> <span class="o">{</span>
</span></span><span class="line"><span class="cl">        <span class="k">try</span><span class="o">{</span>
</span></span><span class="line"><span class="cl">            <span class="n">AsynchronousFileChannel</span> <span class="n">s</span> <span class="o">=</span> 
</span></span><span class="line"><span class="cl">                <span class="n">AsynchronousFileChannel</span><span class="o">.</span><span class="na">open</span><span class="o">(</span>
</span></span><span class="line"><span class="cl">                	<span class="n">Paths</span><span class="o">.</span><span class="na">get</span><span class="o">(</span><span class="s">&#34;1.txt&#34;</span><span class="o">),</span> <span class="n">StandardOpenOption</span><span class="o">.</span><span class="na">READ</span><span class="o">);</span>
</span></span><span class="line"><span class="cl">            <span class="n">ByteBuffer</span> <span class="n">buffer</span> <span class="o">=</span> <span class="n">ByteBuffer</span><span class="o">.</span><span class="na">allocate</span><span class="o">(</span><span class="n">2</span><span class="o">);</span>
</span></span><span class="line"><span class="cl">            <span class="n">log</span><span class="o">.</span><span class="na">debug</span><span class="o">(</span><span class="s">&#34;begin...&#34;</span><span class="o">);</span>
</span></span><span class="line"><span class="cl">            <span class="n">s</span><span class="o">.</span><span class="na">read</span><span class="o">(</span><span class="n">buffer</span><span class="o">,</span> <span class="n">0</span><span class="o">,</span> <span class="kc">null</span><span class="o">,</span> <span class="k">new</span> <span class="n">CompletionHandler</span><span class="o">&lt;</span><span class="n">Integer</span><span class="o">,</span> <span class="n">ByteBuffer</span><span class="o">&gt;()</span> <span class="o">{</span>
</span></span><span class="line"><span class="cl">                <span class="nd">@Override</span>
</span></span><span class="line"><span class="cl">                <span class="kd">public</span> <span class="kt">void</span> <span class="nf">completed</span><span class="o">(</span><span class="n">Integer</span> <span class="n">result</span><span class="o">,</span> <span class="n">ByteBuffer</span> <span class="n">attachment</span><span class="o">)</span> <span class="o">{</span>
</span></span><span class="line"><span class="cl">                    <span class="n">log</span><span class="o">.</span><span class="na">debug</span><span class="o">(</span><span class="s">&#34;read completed...{}&#34;</span><span class="o">,</span> <span class="n">result</span><span class="o">);</span>
</span></span><span class="line"><span class="cl">                    <span class="n">buffer</span><span class="o">.</span><span class="na">flip</span><span class="o">();</span>
</span></span><span class="line"><span class="cl">                    <span class="n">debug</span><span class="o">(</span><span class="n">buffer</span><span class="o">);</span>
</span></span><span class="line"><span class="cl">                <span class="o">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">                <span class="nd">@Override</span>
</span></span><span class="line"><span class="cl">                <span class="kd">public</span> <span class="kt">void</span> <span class="nf">failed</span><span class="o">(</span><span class="n">Throwable</span> <span class="n">exc</span><span class="o">,</span> <span class="n">ByteBuffer</span> <span class="n">attachment</span><span class="o">)</span> <span class="o">{</span>
</span></span><span class="line"><span class="cl">                    <span class="n">log</span><span class="o">.</span><span class="na">debug</span><span class="o">(</span><span class="s">&#34;read failed...&#34;</span><span class="o">);</span>
</span></span><span class="line"><span class="cl">                <span class="o">}</span>
</span></span><span class="line"><span class="cl">            <span class="o">});</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">        <span class="o">}</span> <span class="k">catch</span> <span class="o">(</span><span class="n">IOException</span> <span class="n">e</span><span class="o">)</span> <span class="o">{</span>
</span></span><span class="line"><span class="cl">            <span class="n">e</span><span class="o">.</span><span class="na">printStackTrace</span><span class="o">();</span>
</span></span><span class="line"><span class="cl">        <span class="o">}</span>
</span></span><span class="line"><span class="cl">        <span class="n">log</span><span class="o">.</span><span class="na">debug</span><span class="o">(</span><span class="s">&#34;do other things...&#34;</span><span class="o">);</span>
</span></span><span class="line"><span class="cl">        <span class="n">System</span><span class="o">.</span><span class="na">in</span><span class="o">.</span><span class="na">read</span><span class="o">();</span>
</span></span><span class="line"><span class="cl">    <span class="o">}</span>
</span></span><span class="line"><span class="cl"><span class="o">}</span>
</span></span></code></pre></td></tr></table>
</div>
</div><p>输出</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span><span class="lnt">2
</span><span class="lnt">3
</span><span class="lnt">4
</span><span class="lnt">5
</span><span class="lnt">6
</span><span class="lnt">7
</span><span class="lnt">8
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-fallback" data-lang="fallback"><span class="line"><span class="cl">13:44:56 [DEBUG] [main] c.i.aio.AioDemo1 - begin...
</span></span><span class="line"><span class="cl">13:44:56 [DEBUG] [main] c.i.aio.AioDemo1 - do other things...
</span></span><span class="line"><span class="cl">13:44:56 [DEBUG] [Thread-5] c.i.aio.AioDemo1 - read completed...2
</span></span><span class="line"><span class="cl">         +-------------------------------------------------+
</span></span><span class="line"><span class="cl">         |  0  1  2  3  4  5  6  7  8  9  a  b  c  d  e  f |
</span></span><span class="line"><span class="cl">+--------+-------------------------------------------------+----------------+
</span></span><span class="line"><span class="cl">|00000000| 61 0d                                           |a.              |
</span></span><span class="line"><span class="cl">+--------+-------------------------------------------------+----------------+
</span></span></code></pre></td></tr></table>
</div>
</div><p>可以看到</p>
<ul>
<li>响应文件读取成功的是另一个线程 Thread-5</li>
<li>主线程并没有 IO 操作阻塞</li>
</ul>
<h4 id="-守护线程">💡 守护线程</h4>
<p>默认文件 AIO 使用的线程都是守护线程，所以最后要执行 <code>System.in.read()</code> 以避免守护线程意外结束</p>
<h4 id="网络-aio">网络 AIO</h4>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">  1
</span><span class="lnt">  2
</span><span class="lnt">  3
</span><span class="lnt">  4
</span><span class="lnt">  5
</span><span class="lnt">  6
</span><span class="lnt">  7
</span><span class="lnt">  8
</span><span class="lnt">  9
</span><span class="lnt"> 10
</span><span class="lnt"> 11
</span><span class="lnt"> 12
</span><span class="lnt"> 13
</span><span class="lnt"> 14
</span><span class="lnt"> 15
</span><span class="lnt"> 16
</span><span class="lnt"> 17
</span><span class="lnt"> 18
</span><span class="lnt"> 19
</span><span class="lnt"> 20
</span><span class="lnt"> 21
</span><span class="lnt"> 22
</span><span class="lnt"> 23
</span><span class="lnt"> 24
</span><span class="lnt"> 25
</span><span class="lnt"> 26
</span><span class="lnt"> 27
</span><span class="lnt"> 28
</span><span class="lnt"> 29
</span><span class="lnt"> 30
</span><span class="lnt"> 31
</span><span class="lnt"> 32
</span><span class="lnt"> 33
</span><span class="lnt"> 34
</span><span class="lnt"> 35
</span><span class="lnt"> 36
</span><span class="lnt"> 37
</span><span class="lnt"> 38
</span><span class="lnt"> 39
</span><span class="lnt"> 40
</span><span class="lnt"> 41
</span><span class="lnt"> 42
</span><span class="lnt"> 43
</span><span class="lnt"> 44
</span><span class="lnt"> 45
</span><span class="lnt"> 46
</span><span class="lnt"> 47
</span><span class="lnt"> 48
</span><span class="lnt"> 49
</span><span class="lnt"> 50
</span><span class="lnt"> 51
</span><span class="lnt"> 52
</span><span class="lnt"> 53
</span><span class="lnt"> 54
</span><span class="lnt"> 55
</span><span class="lnt"> 56
</span><span class="lnt"> 57
</span><span class="lnt"> 58
</span><span class="lnt"> 59
</span><span class="lnt"> 60
</span><span class="lnt"> 61
</span><span class="lnt"> 62
</span><span class="lnt"> 63
</span><span class="lnt"> 64
</span><span class="lnt"> 65
</span><span class="lnt"> 66
</span><span class="lnt"> 67
</span><span class="lnt"> 68
</span><span class="lnt"> 69
</span><span class="lnt"> 70
</span><span class="lnt"> 71
</span><span class="lnt"> 72
</span><span class="lnt"> 73
</span><span class="lnt"> 74
</span><span class="lnt"> 75
</span><span class="lnt"> 76
</span><span class="lnt"> 77
</span><span class="lnt"> 78
</span><span class="lnt"> 79
</span><span class="lnt"> 80
</span><span class="lnt"> 81
</span><span class="lnt"> 82
</span><span class="lnt"> 83
</span><span class="lnt"> 84
</span><span class="lnt"> 85
</span><span class="lnt"> 86
</span><span class="lnt"> 87
</span><span class="lnt"> 88
</span><span class="lnt"> 89
</span><span class="lnt"> 90
</span><span class="lnt"> 91
</span><span class="lnt"> 92
</span><span class="lnt"> 93
</span><span class="lnt"> 94
</span><span class="lnt"> 95
</span><span class="lnt"> 96
</span><span class="lnt"> 97
</span><span class="lnt"> 98
</span><span class="lnt"> 99
</span><span class="lnt">100
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-java" data-lang="java"><span class="line"><span class="cl"><span class="kd">public</span> <span class="kd">class</span> <span class="nc">AioServer</span> <span class="o">{</span>
</span></span><span class="line"><span class="cl">    <span class="kd">public</span> <span class="kd">static</span> <span class="kt">void</span> <span class="nf">main</span><span class="o">(</span><span class="n">String</span><span class="o">[]</span> <span class="n">args</span><span class="o">)</span> <span class="kd">throws</span> <span class="n">IOException</span> <span class="o">{</span>
</span></span><span class="line"><span class="cl">        <span class="n">AsynchronousServerSocketChannel</span> <span class="n">ssc</span> <span class="o">=</span> <span class="n">AsynchronousServerSocketChannel</span><span class="o">.</span><span class="na">open</span><span class="o">();</span>
</span></span><span class="line"><span class="cl">        <span class="n">ssc</span><span class="o">.</span><span class="na">bind</span><span class="o">(</span><span class="k">new</span> <span class="n">InetSocketAddress</span><span class="o">(</span><span class="n">8080</span><span class="o">));</span>
</span></span><span class="line"><span class="cl">        <span class="n">ssc</span><span class="o">.</span><span class="na">accept</span><span class="o">(</span><span class="kc">null</span><span class="o">,</span> <span class="k">new</span> <span class="n">AcceptHandler</span><span class="o">(</span><span class="n">ssc</span><span class="o">));</span>
</span></span><span class="line"><span class="cl">        <span class="n">System</span><span class="o">.</span><span class="na">in</span><span class="o">.</span><span class="na">read</span><span class="o">();</span>
</span></span><span class="line"><span class="cl">    <span class="o">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="kd">private</span> <span class="kd">static</span> <span class="kt">void</span> <span class="nf">closeChannel</span><span class="o">(</span><span class="n">AsynchronousSocketChannel</span> <span class="n">sc</span><span class="o">)</span> <span class="o">{</span>
</span></span><span class="line"><span class="cl">        <span class="k">try</span> <span class="o">{</span>
</span></span><span class="line"><span class="cl">            <span class="n">System</span><span class="o">.</span><span class="na">out</span><span class="o">.</span><span class="na">printf</span><span class="o">(</span><span class="s">&#34;[%s] %s close\n&#34;</span><span class="o">,</span> <span class="n">Thread</span><span class="o">.</span><span class="na">currentThread</span><span class="o">().</span><span class="na">getName</span><span class="o">(),</span> <span class="n">sc</span><span class="o">.</span><span class="na">getRemoteAddress</span><span class="o">());</span>
</span></span><span class="line"><span class="cl">            <span class="n">sc</span><span class="o">.</span><span class="na">close</span><span class="o">();</span>
</span></span><span class="line"><span class="cl">        <span class="o">}</span> <span class="k">catch</span> <span class="o">(</span><span class="n">IOException</span> <span class="n">e</span><span class="o">)</span> <span class="o">{</span>
</span></span><span class="line"><span class="cl">            <span class="n">e</span><span class="o">.</span><span class="na">printStackTrace</span><span class="o">();</span>
</span></span><span class="line"><span class="cl">        <span class="o">}</span>
</span></span><span class="line"><span class="cl">    <span class="o">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="kd">private</span> <span class="kd">static</span> <span class="kd">class</span> <span class="nc">ReadHandler</span> <span class="kd">implements</span> <span class="n">CompletionHandler</span><span class="o">&lt;</span><span class="n">Integer</span><span class="o">,</span> <span class="n">ByteBuffer</span><span class="o">&gt;</span> <span class="o">{</span>
</span></span><span class="line"><span class="cl">        <span class="kd">private</span> <span class="kd">final</span> <span class="n">AsynchronousSocketChannel</span> <span class="n">sc</span><span class="o">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">        <span class="kd">public</span> <span class="nf">ReadHandler</span><span class="o">(</span><span class="n">AsynchronousSocketChannel</span> <span class="n">sc</span><span class="o">)</span> <span class="o">{</span>
</span></span><span class="line"><span class="cl">            <span class="k">this</span><span class="o">.</span><span class="na">sc</span> <span class="o">=</span> <span class="n">sc</span><span class="o">;</span>
</span></span><span class="line"><span class="cl">        <span class="o">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">        <span class="nd">@Override</span>
</span></span><span class="line"><span class="cl">        <span class="kd">public</span> <span class="kt">void</span> <span class="nf">completed</span><span class="o">(</span><span class="n">Integer</span> <span class="n">result</span><span class="o">,</span> <span class="n">ByteBuffer</span> <span class="n">attachment</span><span class="o">)</span> <span class="o">{</span>
</span></span><span class="line"><span class="cl">            <span class="k">try</span> <span class="o">{</span>
</span></span><span class="line"><span class="cl">                <span class="k">if</span> <span class="o">(</span><span class="n">result</span> <span class="o">==</span> <span class="o">-</span><span class="n">1</span><span class="o">)</span> <span class="o">{</span>
</span></span><span class="line"><span class="cl">                    <span class="n">closeChannel</span><span class="o">(</span><span class="n">sc</span><span class="o">);</span>
</span></span><span class="line"><span class="cl">                    <span class="k">return</span><span class="o">;</span>
</span></span><span class="line"><span class="cl">                <span class="o">}</span>
</span></span><span class="line"><span class="cl">                <span class="n">System</span><span class="o">.</span><span class="na">out</span><span class="o">.</span><span class="na">printf</span><span class="o">(</span><span class="s">&#34;[%s] %s read\n&#34;</span><span class="o">,</span> <span class="n">Thread</span><span class="o">.</span><span class="na">currentThread</span><span class="o">().</span><span class="na">getName</span><span class="o">(),</span> <span class="n">sc</span><span class="o">.</span><span class="na">getRemoteAddress</span><span class="o">());</span>
</span></span><span class="line"><span class="cl">                <span class="n">attachment</span><span class="o">.</span><span class="na">flip</span><span class="o">();</span>
</span></span><span class="line"><span class="cl">                <span class="n">System</span><span class="o">.</span><span class="na">out</span><span class="o">.</span><span class="na">println</span><span class="o">(</span><span class="n">Charset</span><span class="o">.</span><span class="na">defaultCharset</span><span class="o">().</span><span class="na">decode</span><span class="o">(</span><span class="n">attachment</span><span class="o">));</span>
</span></span><span class="line"><span class="cl">                <span class="n">attachment</span><span class="o">.</span><span class="na">clear</span><span class="o">();</span>
</span></span><span class="line"><span class="cl">                <span class="c1">// 处理完第一个 read 时，需要再次调用 read 方法来处理下一个 read 事件
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>                <span class="n">sc</span><span class="o">.</span><span class="na">read</span><span class="o">(</span><span class="n">attachment</span><span class="o">,</span> <span class="n">attachment</span><span class="o">,</span> <span class="k">this</span><span class="o">);</span>
</span></span><span class="line"><span class="cl">            <span class="o">}</span> <span class="k">catch</span> <span class="o">(</span><span class="n">IOException</span> <span class="n">e</span><span class="o">)</span> <span class="o">{</span>
</span></span><span class="line"><span class="cl">                <span class="n">e</span><span class="o">.</span><span class="na">printStackTrace</span><span class="o">();</span>
</span></span><span class="line"><span class="cl">            <span class="o">}</span>
</span></span><span class="line"><span class="cl">        <span class="o">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">        <span class="nd">@Override</span>
</span></span><span class="line"><span class="cl">        <span class="kd">public</span> <span class="kt">void</span> <span class="nf">failed</span><span class="o">(</span><span class="n">Throwable</span> <span class="n">exc</span><span class="o">,</span> <span class="n">ByteBuffer</span> <span class="n">attachment</span><span class="o">)</span> <span class="o">{</span>
</span></span><span class="line"><span class="cl">            <span class="n">closeChannel</span><span class="o">(</span><span class="n">sc</span><span class="o">);</span>
</span></span><span class="line"><span class="cl">            <span class="n">exc</span><span class="o">.</span><span class="na">printStackTrace</span><span class="o">();</span>
</span></span><span class="line"><span class="cl">        <span class="o">}</span>
</span></span><span class="line"><span class="cl">    <span class="o">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="kd">private</span> <span class="kd">static</span> <span class="kd">class</span> <span class="nc">WriteHandler</span> <span class="kd">implements</span> <span class="n">CompletionHandler</span><span class="o">&lt;</span><span class="n">Integer</span><span class="o">,</span> <span class="n">ByteBuffer</span><span class="o">&gt;</span> <span class="o">{</span>
</span></span><span class="line"><span class="cl">        <span class="kd">private</span> <span class="kd">final</span> <span class="n">AsynchronousSocketChannel</span> <span class="n">sc</span><span class="o">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">        <span class="kd">private</span> <span class="nf">WriteHandler</span><span class="o">(</span><span class="n">AsynchronousSocketChannel</span> <span class="n">sc</span><span class="o">)</span> <span class="o">{</span>
</span></span><span class="line"><span class="cl">            <span class="k">this</span><span class="o">.</span><span class="na">sc</span> <span class="o">=</span> <span class="n">sc</span><span class="o">;</span>
</span></span><span class="line"><span class="cl">        <span class="o">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">        <span class="nd">@Override</span>
</span></span><span class="line"><span class="cl">        <span class="kd">public</span> <span class="kt">void</span> <span class="nf">completed</span><span class="o">(</span><span class="n">Integer</span> <span class="n">result</span><span class="o">,</span> <span class="n">ByteBuffer</span> <span class="n">attachment</span><span class="o">)</span> <span class="o">{</span>
</span></span><span class="line"><span class="cl">            <span class="c1">// 如果作为附件的 buffer 还有内容，需要再次 write 写出剩余内容
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>            <span class="k">if</span> <span class="o">(</span><span class="n">attachment</span><span class="o">.</span><span class="na">hasRemaining</span><span class="o">())</span> <span class="o">{</span>
</span></span><span class="line"><span class="cl">                <span class="n">sc</span><span class="o">.</span><span class="na">write</span><span class="o">(</span><span class="n">attachment</span><span class="o">);</span>
</span></span><span class="line"><span class="cl">            <span class="o">}</span>
</span></span><span class="line"><span class="cl">        <span class="o">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">        <span class="nd">@Override</span>
</span></span><span class="line"><span class="cl">        <span class="kd">public</span> <span class="kt">void</span> <span class="nf">failed</span><span class="o">(</span><span class="n">Throwable</span> <span class="n">exc</span><span class="o">,</span> <span class="n">ByteBuffer</span> <span class="n">attachment</span><span class="o">)</span> <span class="o">{</span>
</span></span><span class="line"><span class="cl">            <span class="n">exc</span><span class="o">.</span><span class="na">printStackTrace</span><span class="o">();</span>
</span></span><span class="line"><span class="cl">            <span class="n">closeChannel</span><span class="o">(</span><span class="n">sc</span><span class="o">);</span>
</span></span><span class="line"><span class="cl">        <span class="o">}</span>
</span></span><span class="line"><span class="cl">    <span class="o">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="kd">private</span> <span class="kd">static</span> <span class="kd">class</span> <span class="nc">AcceptHandler</span> <span class="kd">implements</span> <span class="n">CompletionHandler</span><span class="o">&lt;</span><span class="n">AsynchronousSocketChannel</span><span class="o">,</span> <span class="n">Object</span><span class="o">&gt;</span> <span class="o">{</span>
</span></span><span class="line"><span class="cl">        <span class="kd">private</span> <span class="kd">final</span> <span class="n">AsynchronousServerSocketChannel</span> <span class="n">ssc</span><span class="o">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">        <span class="kd">public</span> <span class="nf">AcceptHandler</span><span class="o">(</span><span class="n">AsynchronousServerSocketChannel</span> <span class="n">ssc</span><span class="o">)</span> <span class="o">{</span>
</span></span><span class="line"><span class="cl">            <span class="k">this</span><span class="o">.</span><span class="na">ssc</span> <span class="o">=</span> <span class="n">ssc</span><span class="o">;</span>
</span></span><span class="line"><span class="cl">        <span class="o">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">        <span class="nd">@Override</span>
</span></span><span class="line"><span class="cl">        <span class="kd">public</span> <span class="kt">void</span> <span class="nf">completed</span><span class="o">(</span><span class="n">AsynchronousSocketChannel</span> <span class="n">sc</span><span class="o">,</span> <span class="n">Object</span> <span class="n">attachment</span><span class="o">)</span> <span class="o">{</span>
</span></span><span class="line"><span class="cl">            <span class="k">try</span> <span class="o">{</span>
</span></span><span class="line"><span class="cl">                <span class="n">System</span><span class="o">.</span><span class="na">out</span><span class="o">.</span><span class="na">printf</span><span class="o">(</span><span class="s">&#34;[%s] %s connected\n&#34;</span><span class="o">,</span> <span class="n">Thread</span><span class="o">.</span><span class="na">currentThread</span><span class="o">().</span><span class="na">getName</span><span class="o">(),</span> <span class="n">sc</span><span class="o">.</span><span class="na">getRemoteAddress</span><span class="o">());</span>
</span></span><span class="line"><span class="cl">            <span class="o">}</span> <span class="k">catch</span> <span class="o">(</span><span class="n">IOException</span> <span class="n">e</span><span class="o">)</span> <span class="o">{</span>
</span></span><span class="line"><span class="cl">                <span class="n">e</span><span class="o">.</span><span class="na">printStackTrace</span><span class="o">();</span>
</span></span><span class="line"><span class="cl">            <span class="o">}</span>
</span></span><span class="line"><span class="cl">            <span class="n">ByteBuffer</span> <span class="n">buffer</span> <span class="o">=</span> <span class="n">ByteBuffer</span><span class="o">.</span><span class="na">allocate</span><span class="o">(</span><span class="n">16</span><span class="o">);</span>
</span></span><span class="line"><span class="cl">            <span class="c1">// 读事件由 ReadHandler 处理
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>            <span class="n">sc</span><span class="o">.</span><span class="na">read</span><span class="o">(</span><span class="n">buffer</span><span class="o">,</span> <span class="n">buffer</span><span class="o">,</span> <span class="k">new</span> <span class="n">ReadHandler</span><span class="o">(</span><span class="n">sc</span><span class="o">));</span>
</span></span><span class="line"><span class="cl">            <span class="c1">// 写事件由 WriteHandler 处理
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>            <span class="n">sc</span><span class="o">.</span><span class="na">write</span><span class="o">(</span><span class="n">Charset</span><span class="o">.</span><span class="na">defaultCharset</span><span class="o">().</span><span class="na">encode</span><span class="o">(</span><span class="s">&#34;server hello!&#34;</span><span class="o">),</span> <span class="n">ByteBuffer</span><span class="o">.</span><span class="na">allocate</span><span class="o">(</span><span class="n">16</span><span class="o">),</span> <span class="k">new</span> <span class="n">WriteHandler</span><span class="o">(</span><span class="n">sc</span><span class="o">));</span>
</span></span><span class="line"><span class="cl">            <span class="c1">// 处理完第一个 accpet 时，需要再次调用 accept 方法来处理下一个 accept 事件
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>            <span class="n">ssc</span><span class="o">.</span><span class="na">accept</span><span class="o">(</span><span class="kc">null</span><span class="o">,</span> <span class="k">this</span><span class="o">);</span>
</span></span><span class="line"><span class="cl">        <span class="o">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">        <span class="nd">@Override</span>
</span></span><span class="line"><span class="cl">        <span class="kd">public</span> <span class="kt">void</span> <span class="nf">failed</span><span class="o">(</span><span class="n">Throwable</span> <span class="n">exc</span><span class="o">,</span> <span class="n">Object</span> <span class="n">attachment</span><span class="o">)</span> <span class="o">{</span>
</span></span><span class="line"><span class="cl">            <span class="n">exc</span><span class="o">.</span><span class="na">printStackTrace</span><span class="o">();</span>
</span></span><span class="line"><span class="cl">        <span class="o">}</span>
</span></span><span class="line"><span class="cl">    <span class="o">}</span>
</span></span><span class="line"><span class="cl"><span class="o">}</span>
</span></span></code></pre></td></tr></table>
</div>
</div></div>

        <div id="donateDiv"><a id="donate">打赏</a></div>
<div class="post-footer" id="post-footer">
    <div class="post-info">
        <div class="post-info-line">
            <div class="post-info-mod">
                <span>更新于 2024-05-26</span>
            </div>
            <div class="post-info-license"></div>
        </div>
        <div class="post-info-line">
            <div class="post-info-md"></div>
            <div class="post-info-share">
                <span></span>
            </div>
        </div>
    </div>

    <div class="post-info-more">
        <section class="post-tags"><i class="fas fa-tags fa-fw"></i>&nbsp;<a href="/tags/%E7%BD%91%E7%BB%9C/">网络</a>,&nbsp;<a href="/tags/netty/">Netty</a></section>
        <section>
            <span><a href="javascript:void(0);" onclick="window.history.back();">返回</a></span>&nbsp;|&nbsp;<span><a href="/">主页</a></span>
        </section>
    </div>

    <div class="post-nav"><a href="/posts/network/http-connect/" class="prev" rel="prev" title="Http建立链接与断开链接"><i class="fas fa-angle-left fa-fw"></i>Http建立链接与断开链接</a>
            <a href="/posts/network/netty02-%E5%85%A5%E9%97%A8/" class="next" rel="next" title="Netty 入门">Netty 入门<i class="fas fa-angle-right fa-fw"></i></a></div>
</div>
<div id="comments"><div id="valine" class="comment"></div><noscript>
                Please enable JavaScript to view the comments powered by <a href="https://valine.js.org/">Valine</a>.
            </noscript></div></article></div>
            </main><footer class="footer">
        <div class="footer-container"><div class="footer-line">由 <a href="https://gohugo.io/" target="_blank" rel="noopener noreffer" title="Hugo 0.102.3">Hugo</a> 强力驱动 | 主题 - <a href="https://github.com/dillonzq/LoveIt" target="_blank" rel="noopener noreffer" title="LoveIt 0.2.10"><i class="far fa-kiss-wink-heart fa-fw"></i> LoveIt</a>
                </div><div class="footer-line"><i class="far fa-copyright fa-fw"></i><span itemprop="copyrightYear">2020 - 2024</span><span class="author" itemprop="copyrightHolder">&nbsp;<a href="https://wlizhi.cc" target="_blank">Wlizhi</a></span><span class="icp-splitter">&nbsp;|&nbsp;</span><br class="icp-br"/>
                    <span class="icp"><a href='https://beian.miit.gov.cn/' target='_blank'>豫ICP备17048163号<a/></span></div>
        </div>
    </footer></div>

        <div id="fixed-buttons"><a href="#" id="back-to-top" class="fixed-button" title="回到顶部">
                <i class="fas fa-arrow-up fa-fw"></i>
            </a><a href="#" id="view-comments" class="fixed-button" title="查看评论">
                <i class="fas fa-comment fa-fw"></i>
            </a>
        </div><link rel="stylesheet" href="/lib/valine/valine.min.css"><script type="text/javascript" src="/lib/valine/Valine.min.js"></script><script type="text/javascript" src="/lib/smooth-scroll/smooth-scroll.min.js"></script><script type="text/javascript" src="/lib/autocomplete/autocomplete.min.js"></script><script type="text/javascript" src="/lib/algoliasearch/algoliasearch-lite.umd.min.js"></script><script type="text/javascript" src="/lib/lazysizes/lazysizes.min.js"></script><script type="text/javascript" src="/lib/clipboard/clipboard.min.js"></script><script type="text/javascript">window.config={"code":{"copyTitle":"复制到剪贴板","maxShownLines":35},"comment":{"valine":{"appId":"4yIHB0Rs1r3ykDVxayI5nqj3-gzGzoHsz","appKey":"ALr4H1hlK94girLDEUCGmp3o","avatar":"mp","el":"#valine","emojiCDN":"https://cdn.jsdelivr.net/npm/emoji-datasource-google@5.0.1/img/google/64/","emojiMaps":{"100":"1f4af.png","alien":"1f47d.png","anger":"1f4a2.png","angry":"1f620.png","anguished":"1f627.png","astonished":"1f632.png","black_heart":"1f5a4.png","blue_heart":"1f499.png","blush":"1f60a.png","bomb":"1f4a3.png","boom":"1f4a5.png","broken_heart":"1f494.png","brown_heart":"1f90e.png","clown_face":"1f921.png","cold_face":"1f976.png","cold_sweat":"1f630.png","confounded":"1f616.png","confused":"1f615.png","cry":"1f622.png","crying_cat_face":"1f63f.png","cupid":"1f498.png","dash":"1f4a8.png","disappointed":"1f61e.png","disappointed_relieved":"1f625.png","dizzy":"1f4ab.png","dizzy_face":"1f635.png","drooling_face":"1f924.png","exploding_head":"1f92f.png","expressionless":"1f611.png","face_vomiting":"1f92e.png","face_with_cowboy_hat":"1f920.png","face_with_hand_over_mouth":"1f92d.png","face_with_head_bandage":"1f915.png","face_with_monocle":"1f9d0.png","face_with_raised_eyebrow":"1f928.png","face_with_rolling_eyes":"1f644.png","face_with_symbols_on_mouth":"1f92c.png","face_with_thermometer":"1f912.png","fearful":"1f628.png","flushed":"1f633.png","frowning":"1f626.png","ghost":"1f47b.png","gift_heart":"1f49d.png","green_heart":"1f49a.png","grimacing":"1f62c.png","grin":"1f601.png","grinning":"1f600.png","hankey":"1f4a9.png","hear_no_evil":"1f649.png","heart":"2764-fe0f.png","heart_decoration":"1f49f.png","heart_eyes":"1f60d.png","heart_eyes_cat":"1f63b.png","heartbeat":"1f493.png","heartpulse":"1f497.png","heavy_heart_exclamation_mark_ornament":"2763-fe0f.png","hole":"1f573-fe0f.png","hot_face":"1f975.png","hugging_face":"1f917.png","hushed":"1f62f.png","imp":"1f47f.png","innocent":"1f607.png","japanese_goblin":"1f47a.png","japanese_ogre":"1f479.png","joy":"1f602.png","joy_cat":"1f639.png","kiss":"1f48b.png","kissing":"1f617.png","kissing_cat":"1f63d.png","kissing_closed_eyes":"1f61a.png","kissing_heart":"1f618.png","kissing_smiling_eyes":"1f619.png","laughing":"1f606.png","left_speech_bubble":"1f5e8-fe0f.png","love_letter":"1f48c.png","lying_face":"1f925.png","mask":"1f637.png","money_mouth_face":"1f911.png","nauseated_face":"1f922.png","nerd_face":"1f913.png","neutral_face":"1f610.png","no_mouth":"1f636.png","open_mouth":"1f62e.png","orange_heart":"1f9e1.png","partying_face":"1f973.png","pensive":"1f614.png","persevere":"1f623.png","pleading_face":"1f97a.png","pouting_cat":"1f63e.png","purple_heart":"1f49c.png","rage":"1f621.png","relaxed":"263a-fe0f.png","relieved":"1f60c.png","revolving_hearts":"1f49e.png","right_anger_bubble":"1f5ef-fe0f.png","robot_face":"1f916.png","rolling_on_the_floor_laughing":"1f923.png","scream":"1f631.png","scream_cat":"1f640.png","see_no_evil":"1f648.png","shushing_face":"1f92b.png","skull":"1f480.png","skull_and_crossbones":"2620-fe0f.png","sleeping":"1f634.png","sleepy":"1f62a.png","slightly_frowning_face":"1f641.png","slightly_smiling_face":"1f642.png","smile":"1f604.png","smile_cat":"1f638.png","smiley":"1f603.png","smiley_cat":"1f63a.png","smiling_face_with_3_hearts":"1f970.png","smiling_imp":"1f608.png","smirk":"1f60f.png","smirk_cat":"1f63c.png","sneezing_face":"1f927.png","sob":"1f62d.png","space_invader":"1f47e.png","sparkling_heart":"1f496.png","speak_no_evil":"1f64a.png","speech_balloon":"1f4ac.png","star-struck":"1f929.png","stuck_out_tongue":"1f61b.png","stuck_out_tongue_closed_eyes":"1f61d.png","stuck_out_tongue_winking_eye":"1f61c.png","sunglasses":"1f60e.png","sweat":"1f613.png","sweat_drops":"1f4a6.png","sweat_smile":"1f605.png","thinking_face":"1f914.png","thought_balloon":"1f4ad.png","tired_face":"1f62b.png","triumph":"1f624.png","two_hearts":"1f495.png","unamused":"1f612.png","upside_down_face":"1f643.png","weary":"1f629.png","white_frowning_face":"2639-fe0f.png","white_heart":"1f90d.png","wink":"1f609.png","woozy_face":"1f974.png","worried":"1f61f.png","yawning_face":"1f971.png","yellow_heart":"1f49b.png","yum":"1f60b.png","zany_face":"1f92a.png","zipper_mouth_face":"1f910.png","zzz":"1f4a4.png"},"enableQQ":true,"highlight":true,"lang":"zh-cn","meta":["nick","mail","link"],"pageSize":5,"placeholder":"昵称栏输入QQ，会使用QQ昵称和头像、并自动补充QQ邮箱到邮箱栏。\r\n正确填写邮箱更容易及时得到回复。\r\n说点什么吧...","recordIP":true,"visitor":true}},"search":{"algoliaAppID":"1U459KF21F","algoliaIndex":"LoveItSite","algoliaSearchKey":"0433ffe95d71201a4f7c3e04b0125ac5","highlightTag":"em","maxResultLength":10,"noResultsFound":"没有找到结果","snippetLength":50,"type":"algolia"}};</script><script type="text/javascript" src="/js/theme.min.js"></script><script type="text/javascript" src="/js/donate.js"></script></body>
</html>
